<!DOCTYPE html>



  


<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>

  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">






  
  
    
      
    
    
      
    
  <script async src="//cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>
  <link href="//cdn.jsdelivr.net/npm/pace-js@1.0.2/themes/blue/pace-theme-minimal.css" rel="stylesheet">








<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />


















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />


<link href="https://fonts.loli.net/css?family=EB+Garamond:400,400i,700,700i|Noto+Serif+SC:400,500,700&display=swap&subset=chinese-simplified" rel="stylesheet">




  

<link href="//cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/blog/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Android,Handler," />





  <link rel="alternate" href="/blog/atom.xml" title="叶金诚的站点" type="application/atom+xml" />






<meta name="description" content="Android百问百答-《那些年被问过的Handler原理》#文章迁移自我的csdn博客 关于Handler，安卓面试最热门的知识点之一。本篇文章将围绕3点展开： 可以提问哪些Questions？ 面试官会怎样follow up？ 以及怎样寻找答案。 Handler常见提问# 哪些场景使用到了Handler？用Handler做什么业务？ 用Handler遇到什么问题？怎么解决这些问题的？ 说一说H">
<meta property="og:type" content="article">
<meta property="og:title" content="AndroidHandler百问百答">
<meta property="og:url" content="http://idolcoder.gitee.io/blog/2020/08/handler%20TOP%20100%20Questions/index.html">
<meta property="og:site_name" content="叶金诚的站点">
<meta property="og:description" content="Android百问百答-《那些年被问过的Handler原理》#文章迁移自我的csdn博客 关于Handler，安卓面试最热门的知识点之一。本篇文章将围绕3点展开： 可以提问哪些Questions？ 面试官会怎样follow up？ 以及怎样寻找答案。 Handler常见提问# 哪些场景使用到了Handler？用Handler做什么业务？ 用Handler遇到什么问题？怎么解决这些问题的？ 说一说H">
<meta property="og:locale">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814220917812.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221037199.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/2020081422110294.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221126574.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221152760.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221240437.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221259759.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221418179.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221532843.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221511481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221456646.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://img-blog.csdnimg.cn/2020081422161443.png#pic_center">
<meta property="og:image" content="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9mLndldGVzdC5xcS5jb20vZ3FvcC8xMDAwMC8yMDAwMC9MYWJJbWFnZV8xNjY0ODA1Yjc3YTAxZDE5MGUwY2Y2MGY2YjkxMGI1MS5naWY">
<meta property="og:image" content="https://img-blog.csdnimg.cn/20200814221558397.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">
<meta property="og:image" content="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9mLndldGVzdC5xcS5jb20vZ3FvcC8xMDAwMC8yMDAwMC9MYWJJbWFnZV83MTliYjQ0YjE3NTZkOTE4OGJkOGExMzQyNTE1NWIzYS5naWY">
<meta property="article:published_time" content="2020-08-10T01:50:49.000Z">
<meta property="article:modified_time" content="2021-03-06T13:51:20.688Z">
<meta property="article:author" content="叶金诚">
<meta property="article:tag" content="Android">
<meta property="article:tag" content="Handler">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://img-blog.csdnimg.cn/20200814220917812.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '',
    scheme: 'Pisces',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":true},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":true,"transition":{"post_block":"flipBounceYIn","post_header":"perspectiveRightIn","post_body":"fadeIn","coll_header":"swoopIn","sidebar":"shrinkIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://idolcoder.gitee.io/blog/2020/08/handler TOP 100 Questions/"/>





<!-- 设置文章需要密码访问 -->
<script>
    (function(){
        if(''){
            if (prompt('请输入文章密码') !== ''){
                alert('密码错误！');
                history.back();
            }
        }
    })();
</script>

  <title>AndroidHandler百问百答 | 叶金诚的站点</title>
  









  

  
	<!-- leafacePlayer -->
	
      <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
      <meta name="baidu-site-verification" content="code-lde50FghqK" />

	

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <!--fork me from github-->
    <a target="_blank" rel="noopener" href="https://github.com/ShaunSheep" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#64CEAA; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">叶金诚的站点</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <h1 class="site-subtitle" itemprop="description">汇贤雅叙</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-movies">
          <a href="/blog/movies/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-film"></i> <br />
            
            电影
          </a>
        </li>
      
        
        <li class="menu-item menu-item-图书">
          <a href="/blog/books/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-calendar"></i> <br />
            
            图书
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-links">
          <a href="/blog/links/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-link"></i> <br />
            
            友链
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/blog/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>


 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://idolcoder.gitee.io/blog/blog/2020/08/handler%20TOP%20100%20Questions/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/head.png">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="叶金诚的站点">
    </span>

    
      <header class="post-header">

        
        
          <h2 class="post-title" itemprop="name headline">AndroidHandler百问百答</h2>
        

        <div class="post-meta">
          <span class="post-time">

             

             

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-08-10T09:50:49+08:00">
                2020-08-10
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Android/" itemprop="url" rel="index">
                    <span itemprop="name">Android</span>
                  </a>
                </span>

                
                
                  ， 
                
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Android/%E9%9D%A2%E8%AF%95/" itemprop="url" rel="index">
                    <span itemprop="name">面试</span>
                  </a>
                </span>

                
                
                  ， 
                
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Android/%E5%BA%94%E7%94%A8%E5%B1%82/" itemprop="url" rel="index">
                    <span itemprop="name">应用层</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          
             <span id="/blog/2020/08/handler%20TOP%20100%20Questions/" class="leancloud_visitors" data-flag-title="AndroidHandler百问百答">
               <span class="post-meta-divider">|</span>
               <span class="post-meta-item-icon">
                 <i class="fa fa-eye"></i>
               </span>
               
                 <span class="post-meta-item-text">浏览&#58;</span>
               
                 <span class="leancloud-visitors-count"></span>
             </span>
          

          <!--
          
          -->

          
            <span class="post-wordcount">
              
                
                  <span class="post-meta-divider">|</span>
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数&#58;</span>
                
                <span title="字数">
                  6.9k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">时长 &asymp;</span>
                
                <span title="时长">
                  30 分钟
                </span>
              
            </span>
          

          <!-- 隐藏文章内标题下，内容描述
          
          -->

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="android百问百答-那些年被问过的handler原理"><a href="#Android百问百答-《那些年被问过的Handler原理》" class="headerlink" title="Android百问百答-《那些年被问过的Handler原理》"></a>Android百问百答-《那些年被问过的Handler原理》<a href="#android百问百答-那些年被问过的handler原理" class="header-anchor">#</a></h1><p>文章迁移自<a target="_blank" rel="noopener" href="https://blog.csdn.net/chivalrousman/article/details/108014580">我的csdn博客</a></p>
<p>关于Handler，安卓面试最热门的知识点之一。本篇文章将围绕3点展开：</p>
<p>可以提问哪些Questions？</p>
<p>面试官会怎样follow up？</p>
<p>以及怎样寻找答案。</p>
<h2 id="handler常见提问"><a href="#Handler常见提问" class="headerlink" title="Handler常见提问"></a>Handler常见提问<a href="#handler常见提问" class="header-anchor">#</a></h2><ol>
<li>哪些场景使用到了Handler？用Handler做什么业务？</li>
<li>用Handler遇到什么问题？怎么解决这些问题的？</li>
<li>说一说Handler原理？</li>
<li>能自己实现一个Handler吗？</li>
<li>说一说Handler延时原理？</li>
<li>Handler延时有哪些缺陷？造成这些缺陷的原因？</li>
<li>你知道Handler#handleMessage原理吗？</li>
<li>Handler的post与sendMessage有哪些区别？</li>
<li>子线程能使用Handler吗？</li>
<li>子线程能创建Handler吗？</li>
<li>了解过HandlerThread吗？</li>
<li>了解过IdleHandler吗？</li>
</ol>
<h2 id="handler常见follow-up"><a href="#Handler常见Follow-Up" class="headerlink" title="Handler常见Follow Up"></a>Handler常见Follow Up<a href="#handler常见follow-up" class="header-anchor">#</a></h2><ol>
<li>你刚才提到了Message，消息屏障听过吗？有几种Message？</li>
<li>Message有什么用？存储了哪些信息？以什么数据结构存储？</li>
<li>APP内最多能有几个Handler？</li>
<li>App内最多能有多少Message？</li>
<li>App内最多能有几个Looper？</li>
<li>App内最多能有几个MessageQueue？</li>
<li>Message如何知道发给哪一个MessageQueue？发给哪个Handler？</li>
<li>MessageQueue如何存储消息，以什么结构存储？</li>
<li>你提到了Looper，请问子线程如何获取Looper？</li>
<li>你提到了Looper，说一说Looper的消息队列模型？</li>
<li>主线程Looper为什么不会阻塞？为什么不会ANR？</li>
<li>子线程跟主线程如何通过Handler通信？</li>
<li>子线程创建Handler这么麻烦，有什么替代方法吗？</li>
<li>主线程Looper什么时候启动的？</li>
<li>对Handler做过哪些优化？</li>
</ol>
<h2 id="handler源码分析"><a href="#Handler源码分析" class="headerlink" title="Handler源码分析"></a>Handler源码分析<a href="#handler源码分析" class="header-anchor">#</a></h2><h3 id="handler构造函数原理"><a href="#Handler-构造函数原理" class="headerlink" title="Handler#构造函数原理"></a>Handler#构造函数原理<a href="#handler构造函数原理" class="header-anchor">#</a></h3><p>Handler有7个构造函数</p>
<pre><code>Handler()
Handler(Handler.Callback callback)
Handler(Looper looper)
Handler(Looper looper, Handler.Callback callback)
Handler(boolean async)
Handler(Callback callback, boolean async)
Handler(Looper looper, Callback callback, boolean async)
</code></pre>
<p>先从其中一个构造函数看起：<br><img src="https://img-blog.csdnimg.cn/20200814220917812.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>提到了MessageQueue、Looper，Message,CallBack暂且记下。</p>
<p>我们根据经验及面试题，关注Handler几个关键API</p>
<ul>
<li>obtainMessage</li>
<li>post</li>
<li>sendMessage</li>
<li>postDelayed</li>
<li>sendMessageDelayed</li>
</ul>
<p>接着我们关注这些API的底层实现，一个一个分析吧！</p>
<h3 id="handlerobtainmessage-原理"><a href="#Handler-obtainMessage-原理" class="headerlink" title="Handler#obtainMessage 原理"></a>Handler#obtainMessage 原理<a href="#handlerobtainmessage-原理" class="header-anchor">#</a></h3><p>Handler#obtainMessage 调用了Message#obtain()</p>
<p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7alw6p4-1597413794308)(C:\Users\lenovo\AppData\Local\Temp\1597329263919.png)]</p>
<p>可以看到obtain函数从Message链表中获取message，这是一种内存复用，节省了频繁创建内存，如果Message链表为空，则创建一个Message。如果你对Message是个链表有疑问，那么请继续看下面的内容吧！</p>
<h3 id="message源码分析"><a href="#Message源码分析" class="headerlink" title="Message源码分析"></a>Message源码分析<a href="#message源码分析" class="header-anchor">#</a></h3><p>Message有如下公有属性，供程序员调用：</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">int</span> what;<span class="comment">//消息标示，</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">int</span> arg1; <span class="comment">//简单int类型数据</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">int</span> arg2;<span class="comment">//简单int类型数据</span></span><br><span class="line"><span class="keyword">public</span> Object obj;<span class="comment">//简单Object类型数据</span></span><br><span class="line"><span class="keyword">public</span> Messenger replyTo;<span class="comment">//跨进程信使</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">int</span> sendingUid = -<span class="number">1</span>;<span class="comment">//Messenger消息标示</span></span><br><span class="line">    </span><br></pre></td></tr></table></figure></div>
<p>Message有如下私有属性,用途如下：</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*package*/</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FLAG_IN_USE = <span class="number">1</span> &lt;&lt; <span class="number">0</span>;<span class="comment">//正在使用中</span></span><br><span class="line"><span class="comment">/*package*/</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FLAG_ASYNCHRONOUS = <span class="number">1</span> &lt;&lt; <span class="number">1</span>;<span class="comment">//消息同步标识</span></span><br><span class="line"><span class="comment">/*package*/</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;<span class="comment">//</span></span><br><span class="line"><span class="comment">/*package*/</span> <span class="keyword">int</span> flags;<span class="comment">//消息执行标识</span></span><br><span class="line"><span class="comment">/*package*/</span> <span class="keyword">long</span> when;<span class="comment">//执行时间</span></span><br><span class="line"><span class="comment">/*package*/</span> Bundle data;<span class="comment">//装载的数据</span></span><br><span class="line"><span class="comment">/*package*/</span> Handler target;<span class="comment">//目标载体</span></span><br><span class="line"><span class="comment">/*package*/</span> Runnable callback;<span class="comment">//任务线程</span></span><br><span class="line"><span class="comment">/*package*/</span> Message next;<span class="comment">//消息链表，下一个消息</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Object sPoolSync = <span class="keyword">new</span> Object();<span class="comment">//锁对象</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Message sPool;<span class="comment">//消息池</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> sPoolSize = <span class="number">0</span>;<span class="comment">//消息池大小</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAX_POOL_SIZE = <span class="number">50</span>;<span class="comment">//消息池最大消息数常量</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">boolean</span> gCheckRecycle = <span class="keyword">true</span>;<span class="comment">//循环检查</span></span><br></pre></td></tr></table></figure></div>
<p>Message的源码，我们可以得出如下结论，Message是一种链表结构，每个Message持有以下信息：</p>
<ol>
<li>用于传递的数据，如what、arg1、arg2、obj</li>
<li>用于执行当前Message的Handler</li>
<li>用于执行当前Message的回调接口CallBack、子线程Runnable</li>
<li>当前Message的属性，如延时时间、执行标识、Bundle数据，下一个Message引用。这种结构构成了链表。</li>
</ol>
<h3 id="handlerpost的原理"><a href="#Handler-post的原理" class="headerlink" title="Handler#post的原理"></a>Handler#post的原理<a href="#handlerpost的原理" class="header-anchor">#</a></h3><p>post函数入口接收一个子线程Runnable对象</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">post</span><span class="params">(Runnable r)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">   <span class="keyword">return</span>  sendMessageDelayed(getPostMessage(r), <span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>getPostMessage()做了如下工作：</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Message <span class="title">getPostMessage</span><span class="params">(Runnable r)</span> </span>&#123;</span><br><span class="line">    Message m = Message.obtain();</span><br><span class="line">    m.callback = r;</span><br><span class="line">    <span class="keyword">return</span> m;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>sendMessageDelayed做了如下工作：</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">sendMessageDelayed</span><span class="params">(Message msg, <span class="keyword">long</span> delayMillis)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (delayMillis &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        delayMillis = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>sendMessageAtTime做了如下工作：</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">sendMessageAtTime</span><span class="params">(Message msg, <span class="keyword">long</span> uptimeMillis)</span> </span>&#123;</span><br><span class="line">    MessageQueue queue = mQueue;</span><br><span class="line">    <span class="keyword">if</span> (queue == <span class="keyword">null</span>) &#123;</span><br><span class="line">        RuntimeException e = <span class="keyword">new</span> RuntimeException(</span><br><span class="line">                <span class="keyword">this</span> + <span class="string">&quot; sendMessageAtTime() called with no mQueue&quot;</span>);</span><br><span class="line">        Log.w(<span class="string">&quot;Looper&quot;</span>, e.getMessage(), e);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> enqueueMessage(queue, msg, uptimeMillis);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>最终走下了MessageQueue#enqueueMessage</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(MessageQueue queue, Message msg, <span class="keyword">long</span> uptimeMillis)</span> </span>&#123;</span><br><span class="line">    msg.target = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">if</span> (mAsynchronous) &#123;</span><br><span class="line">        msg.setAsynchronous(<span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> queue.enqueueMessage(msg, uptimeMillis);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>这一步总结如下</p>
<ol>
<li>getPostMessage将Message与Handler绑定</li>
<li>通过SystemClock.uptimeMillis() + delayMillis计算延时时间，delayMillis默认为0</li>
<li>将Message与计算得出的时间值，传递给MessageQueue#enqueueMessage，交由MessageQueue处理Message。</li>
</ol>
<h3 id="handlersendmessage原理"><a href="#Handler-sendMessage原理" class="headerlink" title="Handler#sendMessage原理"></a>Handler#sendMessage原理<a href="#handlersendmessage原理" class="header-anchor">#</a></h3><p>入口接收Message对象</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">sendMessage</span><span class="params">(Message msg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> sendMessageDelayed(msg, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>后续执行过程与post相同，最终都将Message交由MessageQueue#enqueueMessage处理。</p>
<h3 id="handlerpostdelayed原理"><a href="#Handler-postDelayed原理" class="headerlink" title="Handler#postDelayed原理"></a>Handler#postDelayed原理<a href="#handlerpostdelayed原理" class="header-anchor">#</a></h3><p>与post相比，postDelayed函数入口除了接收Runnable子线程对象，还接收一个时间戳，用于延时时间的计算。其他过程与post相同。</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">postDelayed</span><span class="params">(Runnable r, <span class="keyword">long</span> delayMillis)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> sendMessageDelayed(getPostMessage(r), delayMillis);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="handlersendmessagedelayed原理"><a href="#Handler-sendMessageDelayed原理" class="headerlink" title="Handler#sendMessageDelayed原理"></a>Handler#sendMessageDelayed原理<a href="#handlersendmessagedelayed原理" class="header-anchor">#</a></h3><p>与sendMessage类似，多了一个时间戳，用于计算延时时间。其他过程与sendMessage、相同。</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">sendMessageDelayed</span><span class="params">(Message msg, <span class="keyword">long</span> delayMillis)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (delayMillis &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        delayMillis = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>看完源码，我们得出几个结论：</p>
<ol>
<li>无论是Handler#post或者是Handler#sendMessage，Messag都会交由MessageQueue#enqueueMessage执行</li>
<li>MessageQueue#enqueueMessage接收两个参数Message和long型的时间戳</li>
<li>时间戳计算方式是SystemClock.uptimeMillis() + delayMillis</li>
<li>post与sendMessage的区别是入参参数不一样，post接收Runnable子线程，将子线程绑定到Message上；sendMessage持有的是主线程</li>
</ol>
<p>那么我们心中很自然会产生疑问，MessageQueue#enqueueMessage是如何执行的？</p>
<h2 id="messagequeue源码分析"><a href="#MessageQueue源码分析" class="headerlink" title="MessageQueue源码分析"></a>MessageQueue源码分析<a href="#messagequeue源码分析" class="header-anchor">#</a></h2><p>源码分析的思路是构造函数和enqueueMessage</p>
<h3 id="messagequeue构造函数原理"><a href="#MessageQueue-构造函数原理" class="headerlink" title="MessageQueue#构造函数原理"></a>MessageQueue#构造函数原理<a href="#messagequeue构造函数原理" class="header-anchor">#</a></h3><p>首先来看看构造函数</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MessageQueue(<span class="keyword">boolean</span> quitAllowed) &#123;</span><br><span class="line">    mQuitAllowed = quitAllowed;</span><br><span class="line">    mPtr = nativeInit();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>构造函数之上定义了很多native方法</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">static</span> <span class="keyword">long</span> <span class="title">nativeInit</span><span class="params">()</span></span>;</span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">nativeDestroy</span><span class="params">(<span class="keyword">long</span> ptr)</span></span>;<span class="comment">// 阻塞</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">nativeWake</span><span class="params">(<span class="keyword">long</span> ptr)</span></span>; <span class="comment">// 唤醒</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">nativeIsPolling</span><span class="params">(<span class="keyword">long</span> ptr)</span></span>;</span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">nativeSetFileDescriptorEvents</span><span class="params">(<span class="keyword">long</span> ptr, <span class="keyword">int</span> fd, <span class="keyword">int</span> events)</span></span>;</span><br></pre></td></tr></table></figure></div>
<p>native之上定义了几类数据结构，Message、ArrayList、SparseArray、数组</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Message mMessages; <span class="comment">// 头结点</span></span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">final</span> ArrayList&lt;IdleHandler&gt; mIdleHandlers = <span class="keyword">new</span> ArrayList&lt;IdleHandler&gt;();</span><br><span class="line">   <span class="keyword">private</span> SparseArray&lt;FileDescriptorRecord&gt; mFileDescriptorRecords;</span><br><span class="line">   <span class="keyword">private</span> IdleHandler[] mPendingIdleHandlers;</span><br></pre></td></tr></table></figure></div>
<p>接着我们来看看enqueueMessage是如何处理Message的吧</p>
<h3 id="messagequeueenqueuemessage原理"><a href="#MessageQueue-enqueueMessage原理" class="headerlink" title="MessageQueue#enqueueMessage原理"></a>MessageQueue#enqueueMessage原理<a href="#messagequeueenqueuemessage原理" class="header-anchor">#</a></h3><p><img src="https://img-blog.csdnimg.cn/20200814221037199.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<ul>
<li>图中 1处会判断如果 Message 中的 target 没有被设置，则直接抛出异常；</li>
<li>图中2和 3 处会按照 Message 的时间 when 来有序得插入 MessageQueue 中，可以看出 MessageQueue 实际上是一个链表维护的有序队列，只不过是按照 Message 的执行时间来排序。</li>
</ul>
<p>看到这里，思路似乎终止了，我们跟随Handler、MessQueue的脚步，只看到了Message被插入到MessageQueue的私有队列中。那我们产生的Message什么时候会背消费呢？</p>
<p>视角再次回到一开始的地方——Handler的构造函数原理，在那一节我们提到了Handler构造函数初始化Looper.myLooper()，mLooper.mQueue，接下来我们看看Looper吧！</p>
<h2 id="looper源码分析"><a href="#Looper源码分析" class="headerlink" title="Looper源码分析"></a>Looper源码分析<a href="#looper源码分析" class="header-anchor">#</a></h2><p>查看源码可知，Looper是final类型的，禁止被外部继承修改。</p>
<h3 id="looper子线程用例"><a href="#Looper-子线程用例" class="headerlink" title="Looper#子线程用例"></a>Looper#子线程用例<a href="#looper子线程用例" class="header-anchor">#</a></h3><p>首先在Looper类的注释上，我们看到了如下信息，提示我们在子线程中用个Looper.prepare()+Looper.looper()的方式使用Handler<br><img src="https://img-blog.csdnimg.cn/2020081422110294.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>为什么需要用这种方式开启Looper呢？</p>
<p>答案是在任何线程要开启Loop，都要用Looper.prepare()+Looper.looper()的方式。以APP主进程为例，APP进程启动入口的main方法，也是通过这种方式开启loop的。与子线程细微不同的是，主线程开启looper用的是prepareMainLooper。</p>
<p><strong>ActivityThread #main方法</strong></p>
<p><img src="https://img-blog.csdnimg.cn/20200814221126574.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>带着以下疑问，我们去追看源码：Looper构造函数做了什么？prepare做了什么？loop做了什么？</p>
<h3 id="looper构造函数原理"><a href="#Looper-构造函数原理" class="headerlink" title="Looper#构造函数原理"></a>Looper#构造函数原理<a href="#looper构造函数原理" class="header-anchor">#</a></h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">Looper</span><span class="params">(<span class="keyword">boolean</span> quitAllowed)</span> </span>&#123;</span><br><span class="line">    mQueue = <span class="keyword">new</span> MessageQueue(quitAllowed);</span><br><span class="line">    mThread = Thread.currentThread();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>Looper构造函数做了两件事情，初始化消息队列MessageQueue对象，记录当前线程信息。</p>
<h3 id="loopermylooper原理"><a href="#Looper-myLooper-原理" class="headerlink" title="Looper#myLooper()原理"></a>Looper#myLooper()原理<a href="#loopermylooper原理" class="header-anchor">#</a></h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Return the Looper object associated with the current thread.  Returns</span></span><br><span class="line"><span class="comment"> * null if the calling thread is not associated with a Looper.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="meta">@Nullable</span> <span class="function">Looper <span class="title">myLooper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> sThreadLocal.get();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>可以看到myLooper是从threadLocal中取出Looper对象。在Looper类中定义了如下变量sThreadLocal、mQueue、sMainLooper、mThread</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// sThreadLocal.get() will return null unless you&#x27;ve called prepare().</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> ThreadLocal&lt;Looper&gt; sThreadLocal = <span class="keyword">new</span> ThreadLocal&lt;Looper&gt;();</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Looper sMainLooper;  <span class="comment">// guarded by Looper.class</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> MessageQueue mQueue;</span><br><span class="line"><span class="keyword">final</span> Thread mThread;</span><br></pre></td></tr></table></figure></div>
<h3 id="looperprepare原理"><a href="#Looper-prepare原理" class="headerlink" title="Looper#prepare原理"></a>Looper#prepare原理<a href="#looperprepare原理" class="header-anchor">#</a></h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(<span class="keyword">boolean</span> quitAllowed)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (sThreadLocal.get() != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">&quot;Only one Looper may be created per thread&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    sThreadLocal.set(<span class="keyword">new</span> Looper(quitAllowed));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>prepare就是 new 出一个 Looper。核心之处在于将 new 出的 Looper 设置到了线程本地变量 sThreadLocal 中。也就是说创建的 Looper 与当前线程发生了绑定。</p>
<p>Looper#prepareMainLooper原理</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepareMainLooper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    prepare(<span class="keyword">false</span>);</span><br><span class="line">    <span class="keyword">synchronized</span> (Looper.class) &#123;</span><br><span class="line">        <span class="keyword">if</span> (sMainLooper != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">&quot;The main Looper has already been prepared.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        sMainLooper = myLooper();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>prepareMainLooper只有在APP进程启动的时候有用，并不推荐开发者调用这个函数。</p>
<h3 id="looperloop原理"><a href="#Looper-loop原理" class="headerlink" title="Looper#loop原理"></a>Looper#loop原理<a href="#looperloop原理" class="header-anchor">#</a></h3><p><img src="https://img-blog.csdnimg.cn/20200814221152760.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>图1 取出Looper对象</p>
<p>图2 校验当前线程是否持有Looper，是否启动而来Looper.prepare</p>
<p>图3 从Looper中取出对应的MessageQueue，主线程Looper就取出主线程的MessageQueue，子线程就取出子线程MessageQueue</p>
<p>图4 从MessageQueue中取出Message</p>
<p>图5 Message#target属性，即handler，调用Message绑定好的handler#dispatchMessage，处理消息。</p>
<p>也就是说，Message最终交由与Message绑定的Handler处理。Looper只是负责无限循环+从MessageQueue中读取。</p>
<h3 id="handlerdispatchmessage"><a href="#Handler-dispatchMessage" class="headerlink" title="Handler#dispatchMessage"></a>Handler#dispatchMessage<a href="#handlerdispatchmessage" class="header-anchor">#</a></h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu highlight /plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;**</span><br><span class="line">  * Handle system messages here.</span><br><span class="line">  *&#x2F;</span><br><span class="line"> public void dispatchMessage(Message msg) &#123;</span><br><span class="line">     if (msg.callback !&#x3D; null) &#123;</span><br><span class="line">         handleCallback(msg);&#x2F;&#x2F; 1</span><br><span class="line">     &#125; else &#123;</span><br><span class="line">         if (mCallback !&#x3D; null) &#123;</span><br><span class="line">             if (mCallback.handleMessage(msg)) &#123;&#x2F;&#x2F; 2</span><br><span class="line">                 return;</span><br><span class="line">             &#125;</span><br><span class="line">         &#125;</span><br><span class="line">         handleMessage(msg);&#x2F;&#x2F; 3</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure></div>
<p>可以看到有3处可以处理Message</p>
<p>图 1触发了Message#Runnable的run方法，要知道callback就是个Runnable子线程</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">handleCallback</span><span class="params">(Message message)</span> </span>&#123;</span><br><span class="line">    message.callback.run();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>图2 触发了 Handler#Callback接口，Callback是Handler构造函数初始化的时候传递进来的。参考Handler#构造函数原理</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Callback</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> msg A &#123;<span class="doctag">@link</span> android.os.Message Message&#125; object</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> True if no further handling is desired</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>图3 触发了Handler的handleMessage方法，这是个空实现，一般由开发者复写实现。</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Subclasses must implement this to receive messages.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>在Looper这一节，我们暂停脚步总结一下：</p>
<ol>
<li><p>主线程和子线程都可以使用Handler，Handler使用方式都是要Looper.prepare+Lopper.loop,</p>
</li>
<li><p>子线程Handler用法</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LooperThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">  <span class="keyword">public</span> Handler mHandler;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   Looper.prepare();</span><br><span class="line"></span><br><span class="line">   mHandler = <span class="keyword">new</span> Handler() &#123;</span><br><span class="line">	   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">		   <span class="comment">// process incoming messages here</span></span><br><span class="line">	   &#125;</span><br><span class="line">   &#125;;</span><br><span class="line"></span><br><span class="line">   Looper.loop();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"> &#125;</span><br><span class="line"><span class="keyword">new</span> LooperThread().start();</span><br></pre></td></tr></table></figure></div>


</li>
</ol>
<p>回答这些面试题吧</p>
<h2 id="面试题解析"><a href="#面试题解析" class="headerlink" title="面试题解析"></a>面试题解析<a href="#面试题解析" class="header-anchor">#</a></h2><h3 id="哪些场景使用到了handler用handler做什么业务"><a href="#哪些场景使用到了Handler？用Handler做什么业务？" class="headerlink" title="哪些场景使用到了Handler？用Handler做什么业务？"></a>哪些场景使用到了Handler？用Handler做什么业务？<a href="#哪些场景使用到了handler用handler做什么业务" class="header-anchor">#</a></h3><h4 id="最简单的消息发送"><a href="#最简单的消息发送" class="headerlink" title="最简单的消息发送"></a>最简单的消息发送<a href="#最简单的消息发送" class="header-anchor">#</a></h4><p>主线程使用Handler， 主线程里或子线程里发送消息，或延迟发送消息的方式更新UI如，启动应用时Splash页面的延迟2，3秒后，跳转到主页面</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> WHAT_UPDATE_ICON = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Handler handler = <span class="keyword">new</span> Handler() &#123;</span><br><span class="line">       <span class="meta">@Override</span></span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">               <span class="keyword">case</span> WHAT_UPDATE_ICON:</span><br><span class="line">                   Log.e(Tag, <span class="string">&quot;receive message:&quot;</span> + msg.obj);</span><br><span class="line">                   <span class="keyword">break</span>;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line"> &#125;;</span><br><span class="line">   </span><br><span class="line">   Message msg = handler.obtainMessage(WHAT_UPDATE_ICON);</span><br><span class="line">   msg.obj = <span class="string">&quot;update the imageview&quot;</span>;</span><br><span class="line">   handler.sendMessage(msg);</span><br></pre></td></tr></table></figure></div>
<p>使用消息的时候，尽量使用 obtainMessage 的方式来获取Message，避免多次创建Message对象，消耗内存，效率低下。</p>
<p>记住：消息不一定是更新UI的消息，可以再handlerMessage中做很多事情！</p>
<h4 id="结合handlerthread处理耗时任务"><a href="#结合HandlerThread处理耗时任务" class="headerlink" title="结合HandlerThread处理耗时任务"></a>结合HandlerThread处理耗时任务<a href="#结合handlerthread处理耗时任务" class="header-anchor">#</a></h4><p>结合HandlerThread，串行的处理单个耗时任务，如单任务下载</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DownloadOneByOne</span> <span class="keyword">extends</span> <span class="title">HandlerThread</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DownloadOneByOne</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(DownloadOneByOne.class.getSimpleName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onLooperPrepared</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onLooperPrepared();</span><br><span class="line">        <span class="comment">// 初始化下载组件</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> HandlerThread mHandlerThread;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Handler downloadHandler = <span class="keyword">new</span> Handler()&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.handleMessage(msg);</span><br><span class="line">        String url = (String) msg.obj;</span><br><span class="line">        <span class="comment">// 使用下载组件开始下载</span></span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">initHandler</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化Handler</span></span><br><span class="line">    mHandlerThread = <span class="keyword">new</span> DownloadOneByOne();</span><br><span class="line">    mHandlerThread.start();</span><br><span class="line">    </span><br><span class="line">    downloadHandler = <span class="keyword">new</span> Handler(mHandlerThread.getLooper());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sendDownloadTask</span><span class="params">(String downloadUrl)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 发送下载任务</span></span><br><span class="line">    Message msg = downloadHandler.obtainMessage(WHAT_DOWNLOAD_TASK);</span><br><span class="line">    msg.obj = downloadUrl;</span><br><span class="line">    downloadHandler.sendMessage(msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="倒计时view的简易实现"><a href="#倒计时View的简易实现" class="headerlink" title="倒计时View的简易实现"></a>倒计时View的简易实现<a href="#倒计时view的简易实现" class="header-anchor">#</a></h4><p>通过Handler我们还可以快速简易，并且不占用太多性能的实现一个简易的倒计时View。</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CountDownView</span> <span class="keyword">extends</span> <span class="title">AppCompatTextView</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 总时间</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">long</span> seconds;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 当前分钟</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">long</span> minutes;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 当前秒数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> second = <span class="number">60</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SECONDS_PER_MINUTE = <span class="number">60</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MILLS_PER_SECOND = <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MILLS_PER_MINUTE = SECONDS_PER_MINUTE * <span class="number">1000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> WHAT_DONE = <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> WHAT_TICK = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> marginEnd;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> StringBuilder content = <span class="keyword">new</span> StringBuilder();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">CountDownView</span><span class="params">(Context context, <span class="meta">@Nullable</span> AttributeSet attrs)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context, attrs);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onMeasure</span><span class="params">(<span class="keyword">int</span> widthMeasureSpec, <span class="keyword">int</span> heightMeasureSpec)</span> </span>&#123;</span><br><span class="line">        DeviceProfile deviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile();</span><br><span class="line">        <span class="keyword">int</span> size = (<span class="keyword">int</span>) (MeasureSpec.getSize(widthMeasureSpec) / deviceProfile.inv.numColumns);</span><br><span class="line">        marginEnd = marginEnd == <span class="number">0</span> ? (size - deviceProfile.iconSizePx) / <span class="number">2</span> : marginEnd;</span><br><span class="line"></span><br><span class="line">        setMarginEnd(marginEnd);</span><br><span class="line">        <span class="keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">setMarginEnd</span><span class="params">(<span class="keyword">int</span> marginEnd)</span> </span>&#123;</span><br><span class="line">        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();</span><br><span class="line">        layoutParams.setMarginEnd(marginEnd);</span><br><span class="line">        layoutParams.resolveLayoutDirection(layoutParams.getLayoutDirection());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDetachedFromWindow</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onDetachedFromWindow();</span><br><span class="line">        <span class="keyword">if</span> (handler.hasMessages(WHAT_TICK)) &#123;</span><br><span class="line">            handler.removeMessages(WHAT_TICK);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Handler handler = <span class="keyword">new</span> Handler() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">switch</span> (msg.what) &#123;</span><br><span class="line">                <span class="keyword">case</span> WHAT_DONE:</span><br><span class="line">                    setVisibility(View.GONE);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                    setText(content.toString());</span><br><span class="line">                    handler.post(runnable);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/***</span></span><br><span class="line"><span class="comment">     * 设置倒计时</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> millis</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCountDownMills</span><span class="params">(<span class="keyword">long</span> millis)</span> </span>&#123;</span><br><span class="line">        seconds = (<span class="keyword">long</span>) Math.floor(millis / MILLS_PER_SECOND);</span><br><span class="line">        minutes = (<span class="keyword">long</span>) Math.floor(millis / MILLS_PER_MINUTE) - <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// start after one second</span></span><br><span class="line">        handler.postDelayed(runnable, MILLS_PER_SECOND);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Runnable runnable = <span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (seconds &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                handler.sendEmptyMessage(WHAT_DONE);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            seconds--;</span><br><span class="line">            <span class="keyword">if</span> (second &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                second = SECONDS_PER_MINUTE;</span><br><span class="line">                minutes = (<span class="keyword">long</span>) Math.floor(seconds / SECONDS_PER_MINUTE);</span><br><span class="line">            &#125;</span><br><span class="line">            second--;</span><br><span class="line">            content.delete(<span class="number">0</span>, content.length());</span><br><span class="line"></span><br><span class="line">            appendZeroWhenLower10(minutes);</span><br><span class="line">            content.append(<span class="string">&quot;:&quot;</span>);</span><br><span class="line">            appendZeroWhenLower10(second);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (handler.hasMessages(WHAT_TICK)) &#123;</span><br><span class="line">                handler.removeMessages(WHAT_TICK);</span><br><span class="line">            &#125;</span><br><span class="line">            handler.sendEmptyMessageDelayed(WHAT_TICK, MILLS_PER_SECOND);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> StringBuilder <span class="title">appendZeroWhenLower10</span><span class="params">(<span class="keyword">long</span> value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (value &lt; <span class="number">10</span>) &#123;</span><br><span class="line">            content.append(<span class="string">&quot;0&quot;</span>).append(value);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            content.append(value);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> content;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="结合intentservice的使用"><a href="#结合IntentService的使用" class="headerlink" title="结合IntentService的使用"></a>结合IntentService的使用<a href="#结合intentservice的使用" class="header-anchor">#</a></h4><p>使用IntentService处理耗时的任务相对比较简单，我们来个有难度的，结合AlarmManager的调度，息屏唤醒IntentService定时处理任务的案例来讲</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String ACTION_WAKE_UP = <span class="string">&quot;com.doze.cpu.wakeup&quot;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">registerAlarm</span><span class="params">(Context context, <span class="keyword">int</span> wakeType)</span> </span>&#123;</span><br><span class="line">    type = wakeType;</span><br><span class="line">    <span class="keyword">if</span> (alarmManager == <span class="keyword">null</span>)</span><br><span class="line">        alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (operation != <span class="keyword">null</span>) alarmManager.cancel(operation);</span><br><span class="line"></span><br><span class="line">    schedule(context);</span><br><span class="line">&#125;</span><br><span class="line">    </span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">schedule</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">    Intent intent = <span class="keyword">new</span> Intent();</span><br><span class="line">    intent.setAction(ACTION_WAKE_UP);</span><br><span class="line">    operation = PendingIntent.getBroadcast(context, <span class="number">0</span>, intent, PendingIntent.FLAG_UPDATE_CURRENT);</span><br><span class="line">    <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line">            AlarmUtils.setRTCWakeup(alarmManager, AlarmUtils.DEFAULT_TRIGGER_AT_MILLIS, operation);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">            AlarmUtils.setElapsedWakeup(alarmManager, AlarmUtils.DEFAULT_TRIGGER_AT_MILLIS, operation);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>定时5分钟发送一个Action为com.doze.cpu.wakeup的广播，我们的广播需要继承 WakefulBroadcastReceiver， 在onReceive里，调用startWakefulService方法，会创建一个1分钟的WakeLock，唤醒cpu处理我们的任务，我们的任务IntentService处理最好不过了，处理完就销毁，不会有多余的占用</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WakeCPUReceiver</span> <span class="keyword">extends</span> <span class="title">WakefulBroadcastReceiver</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceive</span><span class="params">(Context context, Intent intent)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        Intent wakefulIntent = <span class="keyword">new</span> Intent(context, WorkService.class);</span><br><span class="line">        startWakefulService(context, wakefulIntent);</span><br><span class="line">        schedule(context);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div>
<p>startWakefulService的源码</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ComponentName <span class="title">startWakefulService</span><span class="params">(Context context, Intent intent)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mActiveWakeLocks) &#123;</span><br><span class="line">            <span class="keyword">int</span> id = mNextId;</span><br><span class="line">            mNextId++;</span><br><span class="line">            <span class="keyword">if</span> (mNextId &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                mNextId = <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            intent.putExtra(EXTRA_WAKE_LOCK_ID, id);</span><br><span class="line">            ComponentName comp = context.startService(intent);</span><br><span class="line">            <span class="keyword">if</span> (comp == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);</span><br><span class="line">            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,</span><br><span class="line">                    <span class="string">&quot;wake:&quot;</span> + comp.flattenToShortString());</span><br><span class="line">            wl.setReferenceCounted(<span class="keyword">false</span>);</span><br><span class="line">            wl.acquire(<span class="number">60</span>*<span class="number">1000</span>);</span><br><span class="line">            mActiveWakeLocks.put(id, wl);</span><br><span class="line">            <span class="keyword">return</span> comp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div>
<p>在IntentService里，我们在onHandleIntent处理我们的任务后，再调用WakefulBroadcastReceiver的静态方法completeWakefulIntent，释放WakeLock，减少电量的消耗</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WorkService</span> <span class="keyword">extends</span> <span class="title">IntentService</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onHandleIntent</span><span class="params">(Intent intent)</span> </span>&#123;</span><br><span class="line">        Log.e(WakeCPUReceiver.TAG, <span class="string">&quot;WorkService is working&quot;</span>);</span><br><span class="line">        <span class="comment">// TODO 处理我们的任务</span></span><br><span class="line">        WakeCPUReceiver.completeWakefulIntent(intent);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<p>completeWakefulIntent源码</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">completeWakefulIntent</span><span class="params">(Intent intent)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (id == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">synchronized</span> (mActiveWakeLocks) &#123;</span><br><span class="line">            PowerManager.WakeLock wl = mActiveWakeLocks.get(id);</span><br><span class="line">            <span class="keyword">if</span> (wl != <span class="keyword">null</span>) &#123;</span><br><span class="line">                wl.release();</span><br><span class="line">                mActiveWakeLocks.remove(id);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// We return true whether or not we actually found the wake lock</span></span><br><span class="line">            <span class="comment">// the return code is defined to indicate whether the Intent contained</span></span><br><span class="line">            <span class="comment">// an identifier for a wake lock that it was supposed to match.</span></span><br><span class="line">            <span class="comment">// We just log a warning here if there is no wake lock found, which could</span></span><br><span class="line">            <span class="comment">// happen for example if this function is called twice on the same</span></span><br><span class="line">            <span class="comment">// intent or the process is killed and restarted before processing the intent.</span></span><br><span class="line">            Log.w(<span class="string">&quot;WakefulBroadcastReceiver&quot;</span>, <span class="string">&quot;No active wake lock id #&quot;</span> + id);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="idlehandler-用于ui性能优化"><a href="#IdleHandler-用于UI性能优化" class="headerlink" title="IdleHandler 用于UI性能优化"></a>IdleHandler 用于UI性能优化<a href="#idlehandler-用于ui性能优化" class="header-anchor">#</a></h4><p>先计算任务放在Activity绘制结束完成之后，节省了90Ms计算时间。参考面试题你了解过IdleHandler 吗？<br><img src="https://img-blog.csdnimg.cn/20200814221240437.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<h4 id="handlerthread用于单线程消息通知器"><a href="#HandlerThread用于单线程消息通知器" class="headerlink" title="HandlerThread用于单线程消息通知器"></a><strong>HandlerThread</strong>用于单线程消息通知器<a href="#handlerthread用于单线程消息通知器" class="header-anchor">#</a></h4><p>在用户操作某些界面元素的时候，如收藏、点赞、转发，有一个小的问题，就是如果有一个操作生成10个快速连续的增删改查操作，那么我们的UI就会收到10次回调，而这种场景下我们其实只需要最后一次回调就够了，中间操作其实不用刷新UI的。如何合并这些频繁操作，只在最后一次操作结束时候响应UI更新呢。</p>
<p>答：HandlerThread+反射MessageQueue+idelHandler<br><img src="https://img-blog.csdnimg.cn/20200814221259759.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<h3 id="用handler遇到什么问题怎么解决这些问题的"><a href="#用Handler遇到什么问题？怎么解决这些问题的？" class="headerlink" title="用Handler遇到什么问题？怎么解决这些问题的？"></a>用Handler遇到什么问题？怎么解决这些问题的？<a href="#用handler遇到什么问题怎么解决这些问题的" class="header-anchor">#</a></h3><h4 id="问题handler延时不准确经常到了时间不响应业务"><a href="#问题：Handler延时不准确，经常到了时间不响应业务" class="headerlink" title="问题：Handler延时不准确，经常到了时间不响应业务"></a>问题：Handler延时不准确，经常到了时间不响应业务<a href="#问题handler延时不准确经常到了时间不响应业务" class="header-anchor">#</a></h4><p>解决：<strong>SystemClock.uptimeMillis()**表示系统</strong>开机到当前的时间总数**，单位是毫秒，但是，当系统进入深度睡眠（CPU休眠、屏幕休眠、设备等待外部输入）时间就会停止，但是不会受到时钟缩放、空闲或者其他节能机制的影响。</p>
<p>使用其他延时方式</p>
<ol>
<li>用concurrent包的TimeUnit类延时sleep()方法延时</li>
<li>Timer+TimeTask</li>
<li>AlarmManager  </li>
<li>ScheduledExecutorService</li>
</ol>
<h4 id="问题子线程创建handler失败"><a href="#问题：子线程创建Handler失败" class="headerlink" title="问题：子线程创建Handler失败"></a>问题：子线程创建Handler失败<a href="#问题子线程创建handler失败" class="header-anchor">#</a></h4><p>解决：参考Looper#子线程用例部分</p>
<h4 id="问题非静态类导致的内存泄漏"><a href="#问题：非静态类导致的内存泄漏" class="headerlink" title="问题：非静态类导致的内存泄漏"></a>问题：非静态类导致的内存泄漏<a href="#问题非静态类导致的内存泄漏" class="header-anchor">#</a></h4><p>解决：static+WeakReference</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyHandler</span> <span class="keyword">extends</span> <span class="title">Handler</span></span>&#123;</span><br><span class="line">     WeakReference&lt;MainActivity&gt; mActivity;</span><br><span class="line">     MyHandler(MainActivity activity)&#123;</span><br><span class="line">         mActivity = <span class="keyword">new</span> WeakReference&lt;MainActivity&gt;(activity);</span><br><span class="line">     &#125;</span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">int</span> cuttent = msg.what;</span><br><span class="line"></span><br><span class="line">        MainActivity activity = mActivity.get();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(activity.currentlayout!=<span class="keyword">null</span>)&#123;</span><br><span class="line">            Animation set2 = <span class="keyword">new</span> TranslateAnimation(<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,-<span class="number">100</span>);</span><br><span class="line">            set2.setDuration(<span class="number">500</span>);</span><br><span class="line">            activity.currentlayout.setAnimation(set2);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        activity.linearLayout.removeAllViews();</span><br><span class="line"></span><br><span class="line">        activity.currentlayout = activity.initView(cuttent);</span><br><span class="line"></span><br><span class="line">        Animation set1 = <span class="keyword">new</span> TranslateAnimation(<span class="number">0</span>,<span class="number">0</span>,<span class="number">100</span>,<span class="number">0</span>);</span><br><span class="line">        set1.setDuration(<span class="number">500</span>);</span><br><span class="line">        activity.currentlayout.setAnimation(set1);</span><br><span class="line"></span><br><span class="line">        activity.linearLayout.addView(activity.currentlayout);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">super</span>.handleMessage(msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="说一说handler原理"><a href="#说一说Handler原理？" class="headerlink" title="说一说Handler原理？"></a>说一说Handler原理？<a href="#说一说handler原理" class="header-anchor">#</a></h3><p>原理的定义是：某个类的提供哪些功能，这些功能是如何实现的？</p>
<p>我认为回答这个问题包含三步：Handler是什么，关键API是什么，关键对象是什么？</p>
<h4 id="第一步回答handler是什么有哪些场景"><a href="#第一步：回答Handler是什么，有哪些场景。" class="headerlink" title="第一步：回答Handler是什么，有哪些场景。"></a>第一步：回答Handler是什么，有哪些场景。<a href="#第一步回答handler是什么有哪些场景" class="header-anchor">#</a></h4><p>答：Handler是Android消息通信组件，用于线程间通信，收发消息，更新UI等参考面试题目1。</p>
<h4 id="第二步handler的关键api是什么用途是什么如何实现的"><a href="#第二步：Handler的关键API是什么，用途是什么，如何实现的。" class="headerlink" title="第二步：Handler的关键API是什么，用途是什么，如何实现的。"></a>第二步：Handler的关键API是什么，用途是什么，如何实现的。<a href="#第二步handler的关键api是什么用途是什么如何实现的" class="header-anchor">#</a></h4><p>答：关键API有：</p>
<p>构造函数：用于绑定MessageQueue、Looper、Message、Runnable、CallBack等</p>
<p>obtainMessage：用于复用Message</p>
<p>post、sendMessage：不同的执行Message方式，前者是接收Runnable参数，后者是当前线程</p>
<p>sendMessageAtTime：消息延时的实现入口，调用MessageQueue#enqueueMessage</p>
<h4 id="第三步handler的关键对象是什么提供哪些功能这些功能如何实现的"><a href="#第三步：Handler的关键对象是什么，提供哪些功能，这些功能如何实现的。" class="headerlink" title="第三步：Handler的关键对象是什么，提供哪些功能，这些功能如何实现的。"></a>第三步：Handler的关键对象是什么，提供哪些功能，这些功能如何实现的。<a href="#第三步handler的关键对象是什么提供哪些功能这些功能如何实现的" class="header-anchor">#</a></h4><p>答：关键对象有：</p>
<p>Message</p>
<p>Message是一种链表结构的子节点，作为载体可以存储的信息有：公开信息arg1、arg2、handler、Message，私有信息when、Runnable。Message有2种Flag，使用中、同步中，三种消息类型，如普通消息、异步消息、消息屏障。</p>
<p>MessageQueue</p>
<p>MessageQueue提供一种链表数据结构，包括头结点信息，插入节点的方式是按照 Message 的时间 when 顺序，时间小的先插入 。</p>
<p>Looper</p>
<p>开启无限循环，不断从 MessageQueue 中取出 Message，然后处理 Message 中指定的任务。典型的Looper是主线程Looper，在 ActivityThread 的 main 方法中，除了调用 Looper.prepareMainLooper 初始化 Looper 对象之外，还调用了 Looper.loop 方法开启无限循环，Looper 的主要功能就是在这个循环中完成的。</p>
<p>Looper提供了一些native方法用于唤醒阻塞状态如nativePollOnce</p>
<p>Looper不断loop的结果，就是调用msg.target.handleMessage,即执行开发者定义好的Handler#handleMessage方法体中的业务。</p>
<h3 id="能自己实现一个handler吗"><a href="#能自己实现一个Handler吗？" class="headerlink" title="能自己实现一个Handler吗？"></a>能自己实现一个Handler吗？<a href="#能自己实现一个handler吗" class="header-anchor">#</a></h3><p>根据Handler的类图，我们可以抽象出Handler消息组件的基本架构。<br><img src="https://img-blog.csdnimg.cn/20200814221418179.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<h4 id="简版handler"><a href="#简版Handler" class="headerlink" title="简版Handler"></a>简版Handler<a href="#简版handler" class="header-anchor">#</a></h4><p>概要设计：</p>
<p>首先我们仿照Android的Handler定义了：阻塞队列、处理消息的回调、分发和发送消息的方法<br>其次然后在创建Handler时，我们获取了当前线程的Looper和MessageQueue</p>
<p>最后，当我们发送消息的时候，将消息添加进之前得到的MessageQueue</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> MyMessageQueue queue;<span class="comment">// 用于进行线程间通信的阻塞队列</span></span><br><span class="line">    <span class="keyword">private</span> CallBack callBack; <span class="comment">// 处理消息的回调</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyHandler</span><span class="params">(CallBack callBack)</span> </span>&#123;</span><br><span class="line">        MyLooper looper = MyLooper.myLooper();</span><br><span class="line">        <span class="keyword">if</span> (looper == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">&quot;在新开的线程中。创建MyHandler对象需要先调用MyLooper.prepare()方法。&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        queue = looper.queue;</span><br><span class="line">        <span class="keyword">this</span>.callBack = callBack;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//消息接收的回调</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">CallBack</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(MyMessage msg)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//发送消息</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendMessage</span><span class="params">(MyMessage msg)</span> </span>&#123;</span><br><span class="line">        msg.target = <span class="keyword">this</span>;</span><br><span class="line">        queue.enqueueMessage(msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//派发消息</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">dispatchMessage</span><span class="params">(MyMessage msg)</span> </span>&#123;</span><br><span class="line">        callBack.handleMessage(msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="简版looper"><a href="#简版Looper" class="headerlink" title="简版Looper"></a>简版Looper<a href="#简版looper" class="header-anchor">#</a></h4><ol>
<li>在Looper中，我们用一个ThreadLocal存储当前Looper的相关数据</li>
<li>定义了一个消息队列，用来管理消息</li>
<li>在prepare()时，用ThreadLocal存储Looper的数据；在myLooper时，读取ThreadLocal存储的Looper数据</li>
<li>在loop()时,用一个死循环来不断的接受和分发消息</li>
</ol>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyLooper</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal&lt;MyLooper&gt; threadLocal = <span class="keyword">new</span> ThreadLocal&lt;&gt;();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> MyLooper myLooper;</span><br><span class="line">    <span class="keyword">public</span> MyMessageQueue queue;<span class="comment">//一个线程对应一个阻塞队列</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">MyLooper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        queue = <span class="keyword">new</span> MyMessageQueue();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//获取当前线程相对应的Looper对象</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> MyLooper <span class="title">myLooper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> threadLocal.get();<span class="comment">//当未调用prepare()方法时。ThreadLocal.get()方法返回的为null;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//为本线程准备对应的MyLooper对象</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (threadLocal.get() != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException( <span class="string">&quot;Only one MyLooper may be created per thread&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        threadLocal.set(<span class="keyword">new</span> MyLooper());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//这里启动消息循环</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">loop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            myLooper = myLooper();</span><br><span class="line">            MyMessageQueue mQueue = myLooper.queue;</span><br><span class="line">            senior.thread_concurrent.handler.MyMessage msg = mQueue.next();<span class="comment">// take()方法是个阻塞方法。线程运行到此会阻塞住。以准备接收发过来的消息</span></span><br><span class="line">            msg.target.dispatchMessage(msg);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="简版messagequeue"><a href="#简版MessageQueue" class="headerlink" title="简版MessageQueue"></a>简版MessageQueue<a href="#简版messagequeue" class="header-anchor">#</a></h4><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyMessageQueue</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> BlockingQueue&lt;MyMessage&gt; queue;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> quit = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyMessageQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        queue = <span class="keyword">new</span> LinkedBlockingQueue&lt;&gt;();</span><br><span class="line">        queue.clear();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//入队</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(MyMessage msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (msg.target == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">&quot;消息必须有一个消息处理者&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            queue.put(msg);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//出队</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> MyMessage <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        MyMessage msg = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (quit) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            msg = queue.take();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> msg;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//销毁</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">quit</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        quit = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="简版message"><a href="#简版Message" class="headerlink" title="简版Message"></a>简版Message<a href="#简版message" class="header-anchor">#</a></h4><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyMessage</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> msg1;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> msg2;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> what;</span><br><span class="line">    <span class="keyword">public</span> Object obj;</span><br><span class="line">    <span class="keyword">public</span> MyHandler target;</span><br><span class="line">    <span class="keyword">public</span> Runnable runnable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h4 id="写一个网络请求的测试用例"><a href="#写一个网络请求的测试用例" class="headerlink" title="写一个网络请求的测试用例"></a>写一个网络请求的测试用例<a href="#写一个网络请求的测试用例" class="header-anchor">#</a></h4><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestClient</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> senior.thread_concurrent.handler.MyHandler mainHandler;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> TestClient().test();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//初始化主线程Looper</span></span><br><span class="line">        MyLooper.prepare();</span><br><span class="line">        mainHandler = <span class="keyword">new</span> senior.thread_concurrent.handler.MyHandler(<span class="keyword">new</span> senior.thread_concurrent.handler.MyHandler.CallBack() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(senior.thread_concurrent.handler.MyMessage msg)</span> </span>&#123;</span><br><span class="line">                <span class="comment">// 刷新界面</span></span><br><span class="line">                String obj = (String) msg.obj;</span><br><span class="line">                LogUtil.print(<span class="string">&quot;刷新界面:&quot;</span> + obj);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">//发起网络请求</span></span><br><span class="line">        LogUtil.print(<span class="string">&quot;在主线程发起一个网络请求&quot;</span>);</span><br><span class="line">        NetThread netThread = <span class="keyword">new</span> NetThread(<span class="string">&quot;http://baidu.com&quot;</span>);</span><br><span class="line">        netThread.start();</span><br><span class="line">        LogUtil.print(<span class="string">&quot;在主线程继续其它操作&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//开始消息循环</span></span><br><span class="line">        MyLooper.loop();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//网络线程类</span></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">NetThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> String url;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">NetThread</span><span class="params">(String url)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.url = url;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            String body = getWebData(url);</span><br><span class="line">            senior.thread_concurrent.handler.MyMessage msg = <span class="keyword">new</span> senior.thread_concurrent.handler.MyMessage();</span><br><span class="line">            msg.obj = body;</span><br><span class="line">            mainHandler.sendMessage(msg);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//执行网络请求</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> String <span class="title">getWebData</span><span class="params">(String url)</span> </span>&#123;</span><br><span class="line">        LogUtil.print(<span class="string">&quot;执行请求网络:&quot;</span> + url);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        String body = <span class="string">&quot;这是&quot;</span> + url + <span class="string">&quot;的响应值&quot;</span>;</span><br><span class="line">        LogUtil.print(<span class="string">&quot;请求网络成功:&quot;</span> + body);</span><br><span class="line">        <span class="keyword">return</span> body;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="说一说handler延时原理"><a href="#说一说Handler延时原理？" class="headerlink" title="说一说Handler延时原理？"></a>说一说Handler延时原理？<a href="#说一说handler延时原理" class="header-anchor">#</a></h3><p>首先Handler无论是post还是sendMessage方式处理Message过程中，都会产生一个时间戳，计算方式是SystemClock.uptimeMillis() + delayMillis，这个时间戳会赋值给Message.when，影响Message在MessageQueue链表中的位置。时间戳值越大，越晚执行。</p>
<p>Handler延时存在时间不准的问题，问题产生原因以及解决办法以及在<u>面试题2问题：Handler延时不准确，经常到了时间不响应业务</u>提到。</p>
<h3 id="handler延时有哪些缺陷造成这些缺陷的原因"><a href="#Handler延时有哪些缺陷？造成这些缺陷的原因？" class="headerlink" title="Handler延时有哪些缺陷？造成这些缺陷的原因？"></a>Handler延时有哪些缺陷？造成这些缺陷的原因？<a href="#handler延时有哪些缺陷造成这些缺陷的原因" class="header-anchor">#</a></h3><p>参考<u>面试题2问题：Handler延时不准确，经常到了时间不响应业务</u></p>
<p>Handler的post与sendMessage有哪些区别？</p>
<p>post需要指定Runnable参数，将传入的Runnable绑定至Handler默认的Message，很多值都为默认值，换言之post方法只是为了执行Runnable子线程的任务。</p>
<p>sendMessage需要传入开发者自定义的Message参数，将Message中的信息载体传递下去，sendMessage方法是为了传递消息。</p>
<p>两者最终都会将Message传递下去，区别是Message中的数据信息赋值数量的不同。</p>
<h3 id="子线程能使用handler吗"><a href="#子线程能使用Handler吗？" class="headerlink" title="子线程能使用Handler吗？"></a>子线程能使用Handler吗？<a href="#子线程能使用handler吗" class="header-anchor">#</a></h3><p>能，可以使用handler对象以及对应的方法。区别是Handler的创建位置，如果Handler在主线程创建，那么只能在主线程中处理消息。如果在子线程创建Handler，那么才能在子线程处理消息。</p>
<h3 id="子线程能创建handler吗"><a href="#子线程能创建Handler吗？" class="headerlink" title="子线程能创建Handler吗？"></a>子线程能创建Handler吗？<a href="#子线程能创建handler吗" class="header-anchor">#</a></h3><p>能，前提是需要Looper.prepare+Looper.loop</p>
<p>Looper.prepare是将当前线程添加到sThreadLocal中，Looper.loop是开启无限循环，不断执行Message</p>
<h3 id="子线程创建handler这么麻烦有什么替代方法吗了解过handlerthread吗"><a href="#子线程创建Handler这么麻烦，有什么替代方法吗？了解过HandlerThread吗？" class="headerlink" title="子线程创建Handler这么麻烦，有什么替代方法吗？了解过HandlerThread吗？"></a>子线程创建Handler这么麻烦，有什么替代方法吗？了解过HandlerThread吗？<a href="#子线程创建handler这么麻烦有什么替代方法吗了解过handlerthread吗" class="header-anchor">#</a></h3><p>HandlerThread的run方法中替我们做了Looper.prepare+Looper.loop</p>
<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">HandlerThread handlerThread = <span class="keyword">new</span> HandlerThread(<span class="string">&quot;handler-thread&quot;</span>);</span><br><span class="line">handlerThread.start(); <span class="comment">// 必须在Handler创建前调用，因为线程start后才会创建Looper</span></span><br><span class="line"></span><br><span class="line">Handler threadHandler = <span class="keyword">new</span> Handler(handlerThread.getLooper()) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.handleMessage(msg);</span><br><span class="line">        <span class="comment">// 处理消息，因为这个方法是在子线程调用，所以可以在这执行耗时任务</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div>
<h3 id="了解过idlehandler吗"><a href="#了解过IdleHandler吗？" class="headerlink" title="了解过IdleHandler吗？"></a>了解过IdleHandler吗？<a href="#了解过idlehandler吗" class="header-anchor">#</a></h3><p>IdleHandler 用途：</p>
<ol>
<li>IdleHandler 是 Handler 提供的一种在消息队列空闲时，执行任务的时机；</li>
<li>当 MessageQueue 当前没有立即需要处理的消息时，会执行 IdleHandler；</li>
<li>Activity界面绘制结束的回调时机</li>
</ol>
<p>IdleHandler 缺点：</p>
<ol>
<li>但它执行的时机依赖消息队列的情况，那么如果 MessageQueue 一直有待执行的消息时，IdleHandler 就一直得不到执行，也就是它的执行时机是不可控的，不适合执行一些对时机要求比较高的任务。</li>
</ol>
<p>IdleHandler场景</p>
<p>如果我们想在界面绘制出来后做点什么，那么在onResume里面是不合适的，它先于measure等流程了<strong>， **有人可能会说在onResume里面post一个runnable可以吗？还是不行，因为那样就会变成这个样子</strong></p>
<p><img src="https://img-blog.csdnimg.cn/20200814221532843.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>所以你的行为一样会在绘制之前执行，这个时候我们的主角IdleHandler就发挥作用了，我们前面说了，它是在looper里面message暂时执行完毕了就会回调，顾名思义嘛，Idle就是队列为空的意思，那么我们的onResume和measure, layout, draw都是一个个message的话，这个IdleHandler就提供了一个它们都执行完毕的回调了，大概就是这样<br><img src="https://img-blog.csdnimg.cn/20200814221511481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>也就是说IdleHandler可以再界面绘制的消息回调之后执行。<br><img src="https://img-blog.csdnimg.cn/20200814221456646.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p>优化前：</p>
<p>这个是我们地图的公交详情页面， 进入之后产品要求左边的页卡需要展示，可以看到左边的页卡是一个非常复杂的布局，那么进入之后的效果可以明显看到头部的展示信息是先显示空白再100毫秒左右之后才展示出来的，原因就是这个页卡的内容比较复杂，用数据向它填充的时候花了较长时间，代码如下：<br><img src="https://img-blog.csdnimg.cn/2020081422161443.png#pic_center" alt="在这里插入图片描述"></p>
<p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9mLndldGVzdC5xcS5jb20vZ3FvcC8xMDAwMC8yMDAwMC9MYWJJbWFnZV8xNjY0ODA1Yjc3YTAxZDE5MGUwY2Y2MGY2YjkxMGI1MS5naWY"></p>
<p>可以看到这个detailView就是这个侧滑的页卡了，填充里面的数据花了90ms，如果这个时间是用在了界面view绘制之前的话，就会出现以上的效果了，view先是白的，再出现，这样就体验不好了。</p>
<p>优化后：如果我们把它放到IdleHandler里面呢？</p>
<p>结果非常明显：顶部的页卡先展示出来了，这样体验是不是会更好一些呢。虽然只有短短90ms，不过我们做app也应该关注这种细节优化的，是吧~ 这个做法也提供了一种思路，android本身提供的activity框架和fragment框架并没有提供绘制完成的回调，如果我们自己实现一个框架，就可以使用这个IdleHandler来实现一个onRenderFinished这种回调了。</p>
<p>代码如下：</p>
<p><img src="https://img-blog.csdnimg.cn/20200814221558397.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaXZhbHJvdXNtYW4=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"></p>
<p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9mLndldGVzdC5xcS5jb20vZ3FvcC8xMDAwMC8yMDAwMC9MYWJJbWFnZV83MTliYjQ0YjE3NTZkOTE4OGJkOGExMzQyNTE1NWIzYS5naWY"><br>特别参考</p>
<p><a target="_blank" rel="noopener" href="https://wetest.qq.com/lab/view/352.html">https://wetest.qq.com/lab/view/352.html</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/u013718120/article/details/51945490">https://blog.csdn.net/u013718120/article/details/51945490</a></p>
<link rel="stylesheet" href="/css/bilicard.css" type="text/css">

        

      
    </div>

      <!-- 相关文章推荐 -->
     
          

  <div class="popular-posts-header">
    <i class="fa fa-graduation-cap"></i>
    相关文章
  </div>

 <details>
   <summary>点击查看</summary>
     <ul class="popular-posts">
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\05\The-most-comprehensive-thinking-of-crash-practice-strategy\" rel="bookmark">Android Crash实践策略最全面思考</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\05\android-crash-who-kill-you\" rel="bookmark">Android Crash揭秘与实践</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\02\mtk-log\" rel="bookmark">mtk-log</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\02\android-bugfree-three-lines\" rel="bookmark">BugFree三剑客异常、日志、工具</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\02\Client-Skeleton-Screen\" rel="bookmark">Android骨架屏详解</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\01\Android-plug-in-technology-selection\" rel="bookmark">Android插件化技术选型</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\01\Android-component-Practice-Guide\" rel="bookmark">Android组件化实践指南</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2021\01\android-structure-best-practice\" rel="bookmark">Android大厂架构设计最佳实践</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2018\02\MVC-MVP-MVVM -of -architecture -selection\" rel="bookmark">架构选型之MVC、MVP、MVVM</a></div>
         
       </li>
     
       <li class="popular-posts-item">
         
         
         <div class="popular-posts-title"><a href="\blog\2016\06\android-communication\" rel="bookmark">详解 Android 通信</a></div>
         
       </li>
     
     </ul>
 </details>



     

    
    
    

    <div>
          
            

          
    </div>

    

    <div>
      
        <div>
    
        <div class="read-over">-------------------本文结束 <i class="fa fa-paw"></i> 感谢您的阅读-------------------</div>
    
</div>

      
    </div>

    
      <div>
        <div class="share_reward">
  <div>坚持原创技术分享，感谢您的支持和鼓励！</div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none;">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="http://cdn.yangchaofan.cn/BlogGifRes/20210313/s7mteFUqc8pH.png" alt=" 微信支付"/>
        <p>微信支付</p>
      </div>
    

    
      <div id="alipay" style="display: inline-block">
        <img id="alipay_qr" src="http://cdn.yangchaofan.cn/BlogGifRes/20210313/oQnokBcx6RUO.png" alt=" 支付宝"/>
        <p>支付宝</p>
      </div>
    

    

  </div>
</div>

      </div>
    

    
      <div>
        <ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者：</strong>
    叶金诚
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://idolcoder.gitee.io/blog/2020/08/handler%20TOP%20100%20Questions/" title="AndroidHandler百问百答">http://idolcoder.gitee.io/blog/2020/08/handler%20TOP%20100%20Questions/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>
    本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh" rel="external nofollow" target="_blank">Creative Commons BY-NC-ND 3.0</a> 许可协议。转载请注明出处！
  </li>
</ul>

      </div>
    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/blog/tags/Android/" rel="tag"> <i class="fa fa-tag"></i> Android</a>
          
            <a href="/blog/tags/Handler/" rel="tag"> <i class="fa fa-tag"></i> Handler</a>
          
        </div>
      

      
      
        <div class="post-widgets">
        

        

        
          
          <div id="needsharebutton-postbottom">
            <span class="btn">
              <i class="fa fa-share-alt" aria-hidden="true"></i>
            </span>
          </div>
        
        </div>
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/blog/2021/01/android-structure-best-practice/" rel="prev" title="Android大厂架构设计最佳实践">
                <i class="fa fa-chevron-left"></i> Android大厂架构设计最佳实践
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/blog/2019/06/android-hotfix/" rel="next" title="Android 热补丁技术详解大全">
                Android 热补丁技术详解大全 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
        
  <script>
    window._bd_share_config = {
      "common": {
        "bdText": "",
        "bdMini": "1",
        "bdMiniList": false,
        "bdPic": ""
      },
      "image": {
        "viewList": ["tsina", "douban", "sqq", "qzone", "weixin", "twi", "fbook"],
        "viewText": "分享到：",
        "viewSize": "16"
      },
      "slide": {
        "bdImg": "5",
        "bdPos": "left",
        "bdTop": "100"
      }
    }
  </script>

<script>
//  with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='//bdimg.share.baidu.com/static/api/js/share.js?cdnversion='+~(-new Date()/36e5)];
//    with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='/static/api/js/share.js?cdnversion='+~(-new Date()/36e5)];
</script>

      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
      <div id="lv-container" data-id="city" data-uid="MTAyMC81Mjc2OC8yOTI0NQ"></div>
    </div>

  





        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
      <div id="sidebar-dimmer"></div>
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <a href="/">
              <img class="site-author-image" itemprop="image"
                src="/blog/images/head.png"
                alt="" />
              </a>
            
              <p class="site-author-name" itemprop="name"></p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/blog/archives/">
              
                  <span class="site-state-item-count">24</span>
                  <span class="site-state-item-name">文章</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/blog/categories/index.html">
                  <span class="site-state-item-count">16</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/blog/tags/index.html">
                  <span class="site-state-item-count">14</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/blog/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
              <!-- 为Hexo Next主题添加哈林摇特效  -->
              <a title="收藏到书签，偶尔High一下^_^" rel="alternate" class="mw-harlem_shake_slow wobble shake" href="javascript:void(0)" onclick="javascript:(function go() {function c() {var e = document.createElement('link');e.setAttribute('type', 'text/css');e.setAttribute('rel', 'stylesheet');e.setAttribute('href', f);e.setAttribute('class', l);document.body.appendChild(e)}function h(){var e = document.getElementsByClassName(l);for(var t = 0; t< e.length; t++){document.body.removeChild(e[t])}}function p(){var e = document.createElement('div');e.setAttribute('class', a);document.body.appendChild(e);setTimeout(function(){document.body.removeChild(e)},100)}function d(e){return{height:e.offsetHeight,width:e.offsetWidth}}function v(i){var s = d(i);return s.height>e &amp;&amp;s.height<n &amp;&amp; s.width>t &amp;&amp;s.width<r}function m(e){var t=e;var n=0;while(!!t){n+=t.offsetTop;t=t.offsetParent}return n}function g(){var e=document.documentElement;if(!!window.innerWidth){return window.innerHeight}else if(e &amp;&amp; !isNaN(e.clientHeight)){return e.clientHeight}return 0}function y(){if(window.pageYOffset){return window.pageYOffset}return Math.max(document.documentElement.scrollTop,document.body.scrollTop)}function E(e){var t=m(e);return t>=w &amp;&amp; t<=b+w}var songs=['https://www.liaofuzhan.com/music/无尽光芒.mp3'];function S(){var e=document.getElementById('audio_element_id');if(e!=null){var index=parseInt(e.getAttribute('curSongIndex'));if(index>songs.length-2){index=0;}else{index++;}e.setAttribute('curSongIndex',index);N();}e.src=i;e.play()}function x(e){e.className+=' '+s+' '+o}function T(e){e.className+=' '+s+' '+u[Math.floor(Math.random()*u.length)]}function N(){var e=document.getElementsByClassName(s);var t=new RegExp('\\b'+s+'\\b');for(var n=0;n<e.length;){e[n].className=e[n].className.replace(t,'')}}function initAudioEle(){var e=document.getElementById('audio_element_id');if(e===null){e=document.createElement('audio');e.setAttribute('class',l);e.setAttribute('curSongIndex',0);e.id='audio_element_id';e.loop=false;e.bgcolor=0;e.addEventListener('canplay',function(){setTimeout(function(){x(k)},500);setTimeout(function(){N();p();for(var e=0;e<O.length;e++){T(O[e])}},15500)},true);e.addEventListener('ended',function(){N();h();go();},true);e.innerHTML='<p>If you are reading this,it is because your browser does not support the audio element. We recommend that you get a new browser.</p><p>';document.body.appendChild(e);}}initAudioEle();var e=30;var t=30;var n=350;var r=350;var curSongIndex=parseInt(document.getElementById('audio_element_id').getAttribute('curSongIndex'));var i=songs[curSongIndex];var s='mw-harlem_shake_me';var o='im_first';var u=['im_drunk','im_baked','im_trippin','im_blown'];var a='mw-strobe_light';var f='https://s3.amazonaws.com/moovweb-marketing/playground/harlem-shake-style.css';var l='mw_added_css';var b=g();var w=y();var C=document.getElementsByTagName('*');var k=null;for(var L=0;L<C.length;L++){var A=C[L];if(v(A)){if(E(A)){k=A;break}}}if(A===null){console.warn('Could not find a node of the right size. Please try a different page.');return}c();S();var O=[];for(var L=0;L<C.length;L++){var A=C[L];if(v(A)){O.push(A)}}})()"><i class="fa fa-music"></i> High~</a>

            </div>
          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a rel="external nofollow" href="https://github.com/ShaunSheep" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i></a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a rel="external nofollow" href="mailto:zuogewoniu@qq.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i></a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a rel="external nofollow" href="https://www.douban.com/people/jincheng-ycf/" target="_blank" title="豆瓣">
                      
                        <i class="fa fa-fw fa-stack-overflow"></i></a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a rel="external nofollow" href="https://www.zhihu.com/people/OscarWildeHawking" target="_blank" title="知乎">
                      
                        <i class="fa fa-fw fa-instagram"></i></a>
                  </span>
                
            </div>
          

            <!--
            <div id="music163player">
                <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="//music.163.com/outchain/player?type=2&id=1336790004&auto=1&height=66"></iframe>
            </div>
            -->

          
          

          
          

          <!--近期文章版块 began-->
          
              <div class="links-of-blogroll motion-element links-of-blogroll-block">
                <div class="links-of-blogroll-title">
                  <i class="fa fa-history fa-" aria-hidden="true"></i>
                  近期文章
                </div>
                <ul class="links-of-blogroll-list">
                  
                  
                    <li class='my-links-of-blogroll-li'>
                      <a href="/blog/2021/05/The-most-comprehensive-thinking-of-crash-practice-strategy/" title="Android Crash实践策略最全面思考" target="_blank">Android Crash实践策略最全面思考</a>
                    </li>
                  
                    <li class='my-links-of-blogroll-li'>
                      <a href="/blog/2021/05/android-crash-who-kill-you/" title="Android Crash揭秘与实践" target="_blank">Android Crash揭秘与实践</a>
                    </li>
                  
                    <li class='my-links-of-blogroll-li'>
                      <a href="/blog/2021/04/Career-stability-experience/" title="职业稳定性杂谈" target="_blank">职业稳定性杂谈</a>
                    </li>
                  
                    <li class='my-links-of-blogroll-li'>
                      <a href="/blog/2021/02/mtk-log/" title="mtk-log" target="_blank">mtk-log</a>
                    </li>
                  
                    <li class='my-links-of-blogroll-li'>
                      <a href="/blog/2021/02/android-bugfree-three-lines/" title="BugFree三剑客异常、日志、工具" target="_blank">BugFree三剑客异常、日志、工具</a>
                    </li>
                  
                </ul>
              </div>
          
          <!--近期文章版块 end-->

          
              <!-- canvas粒子时钟 -->
              <!-- canvas粒子时钟 https://www.cnblogs.com/xiaohuochai/p/6368039.html
  https://www.html5tricks.com/html5-canvas-dance-time.html
 -->
<div id="">
  <canvas id="canvas" style="width:60%;">
</div>
<script async>
(function(){
  var WINDOW_WIDTH = 820;
  		var WINDOW_HEIGHT = 250;
  		var RADIUS = 7; //球半径
  		var NUMBER_GAP = 10; //数字之间的间隙
  		var u=0.65; //碰撞能量损耗系数
  		var context; //Canvas绘制上下文
  		var balls = []; //存储彩色的小球
  		const colors = ["#33B5E5","#0099CC","#AA66CC","#9933CC","#99CC00","#669900","#FFBB33","#FF8800","#FF4444","#CC0000"]; //彩色小球的颜色
  		var currentNums = []; //屏幕显示的8个字符
  		var digit =
                  [
                      [
                          [0,0,1,1,1,0,0],
                          [0,1,1,0,1,1,0],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [0,1,1,0,1,1,0],
                          [0,0,1,1,1,0,0]
                      ],//0
                      [
                          [0,0,0,1,1,0,0],
                          [0,1,1,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [1,1,1,1,1,1,1]
                      ],//1
                      [
                          [0,1,1,1,1,1,0],
                          [1,1,0,0,0,1,1],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,1,1,0],
                          [0,0,0,1,1,0,0],
                          [0,0,1,1,0,0,0],
                          [0,1,1,0,0,0,0],
                          [1,1,0,0,0,0,0],
                          [1,1,0,0,0,1,1],
                          [1,1,1,1,1,1,1]
                      ],//2
                      [
                          [1,1,1,1,1,1,1],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,1,1,0],
                          [0,0,0,1,1,0,0],
                          [0,0,1,1,1,0,0],
                          [0,0,0,0,1,1,0],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [0,1,1,1,1,1,0]
                      ],//3
                      [
                          [0,0,0,0,1,1,0],
                          [0,0,0,1,1,1,0],
                          [0,0,1,1,1,1,0],
                          [0,1,1,0,1,1,0],
                          [1,1,0,0,1,1,0],
                          [1,1,1,1,1,1,1],
                          [0,0,0,0,1,1,0],
                          [0,0,0,0,1,1,0],
                          [0,0,0,0,1,1,0],
                          [0,0,0,1,1,1,1]
                      ],//4
                      [
                          [1,1,1,1,1,1,1],
                          [1,1,0,0,0,0,0],
                          [1,1,0,0,0,0,0],
                          [1,1,1,1,1,1,0],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [0,1,1,1,1,1,0]
                      ],//5
                      [
                          [0,0,0,0,1,1,0],
                          [0,0,1,1,0,0,0],
                          [0,1,1,0,0,0,0],
                          [1,1,0,0,0,0,0],
                          [1,1,0,1,1,1,0],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [0,1,1,1,1,1,0]
                      ],//6
                      [
                          [1,1,1,1,1,1,1],
                          [1,1,0,0,0,1,1],
                          [0,0,0,0,1,1,0],
                          [0,0,0,0,1,1,0],
                          [0,0,0,1,1,0,0],
                          [0,0,0,1,1,0,0],
                          [0,0,1,1,0,0,0],
                          [0,0,1,1,0,0,0],
                          [0,0,1,1,0,0,0],
                          [0,0,1,1,0,0,0]
                      ],//7
                      [
                          [0,1,1,1,1,1,0],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [0,1,1,1,1,1,0],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [0,1,1,1,1,1,0]
                      ],//8
                      [
                          [0,1,1,1,1,1,0],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [1,1,0,0,0,1,1],
                          [0,1,1,1,0,1,1],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,0,1,1],
                          [0,0,0,0,1,1,0],
                          [0,0,0,1,1,0,0],
                          [0,1,1,0,0,0,0]
                      ],//9
                      [
                          [0,0,0,0],
                          [0,0,0,0],
                          [0,1,1,0],
                          [0,1,1,0],
                          [0,0,0,0],
                          [0,0,0,0],
                          [0,1,1,0],
                          [0,1,1,0],
                          [0,0,0,0],
                          [0,0,0,0]
                      ]//:
                  ];

  		function drawDatetime(cxt){
  			var nums = [];

  			context.fillStyle="#005eac"
  			var date = new Date();
  			var offsetX = 70, offsetY = 30;
  			var hours = date.getHours();
  			var num1 = Math.floor(hours/10);
  			var num2 = hours%10;
  			nums.push({num: num1});
  			nums.push({num: num2});
  			nums.push({num: 10}); //冒号
  			var minutes = date.getMinutes();
  			var num1 = Math.floor(minutes/10);
  			var num2 = minutes%10;
  			nums.push({num: num1});
  			nums.push({num: num2});
  			nums.push({num: 10}); //冒号
  			var seconds = date.getSeconds();
  			var num1 = Math.floor(seconds/10);
  			var num2 = seconds%10;
  			nums.push({num: num1});
  			nums.push({num: num2});

  			for(var x = 0;x<nums.length;x++){
  				nums[x].offsetX = offsetX;
  				offsetX = drawSingleNumber(offsetX,offsetY, nums[x].num,cxt);
  				//两个数字连一块，应该间隔一些距离
  				if(x<nums.length-1){
  					if((nums[x].num!=10) &&(nums[x+1].num!=10)){
  						offsetX+=NUMBER_GAP;
  					}
  				}
  			}

  			//说明这是初始化
  			if(currentNums.length ==0){
  				currentNums = nums;
  			}else{
  				//进行比较
  				for(var index = 0;index<currentNums.length;index++){
  					if(currentNums[index].num!=nums[index].num){
  						//不一样时，添加彩色小球
  						addBalls(nums[index]);
  						currentNums[index].num=nums[index].num;
  					}
  				}
  			}
  			renderBalls(cxt);
  			updateBalls();

  			return date;
  		}

  		function addBalls (item) {
  			var num = item.num;
  			var numMatrix = digit[num];
  			for(var y = 0;y<numMatrix.length;y++){
  				for(var x = 0;x<numMatrix[y].length;x++){
  					if(numMatrix[y][x]==1){
  						var ball={
  							offsetX:item.offsetX+RADIUS+RADIUS*2*x,
  							offsetY:30+RADIUS+RADIUS*2*y,
  							color:colors[Math.floor(Math.random()*colors.length)],
  							g:1.5+Math.random(),
  							vx:Math.pow(-1, Math.ceil(Math.random()*10))*4+Math.random(),
  							vy:-5
  						}
  						balls.push(ball);
  					}
  				}
  			}
  		}

  		function renderBalls(cxt){
  			for(var index = 0;index<balls.length;index++){
  				cxt.beginPath();
  				cxt.fillStyle=balls[index].color;
  				cxt.arc(balls[index].offsetX, balls[index].offsetY, RADIUS, 0, 2*Math.PI);
  				cxt.fill();
  			}
  		}

  		function updateBalls () {
  			var i =0;
  			for(var index = 0;index<balls.length;index++){
  				var ball = balls[index];
  				ball.offsetX += ball.vx;
  				ball.offsetY += ball.vy;
  				ball.vy+=ball.g;
  				if(ball.offsetY > (WINDOW_HEIGHT-RADIUS)){
  					ball.offsetY= WINDOW_HEIGHT-RADIUS;
  					ball.vy=-ball.vy*u;
  				}
  				if(ball.offsetX>RADIUS&&ball.offsetX<(WINDOW_WIDTH-RADIUS)){

  					balls[i]=balls[index];
  					i++;
  				}
  			}
  			//去除出边界的球
  			for(;i<balls.length;i++){
  				balls.pop();
  			}
  		}
  		function drawSingleNumber(offsetX, offsetY, num, cxt){
  			var numMatrix = digit[num];
  			for(var y = 0;y<numMatrix.length;y++){
  				for(var x = 0;x<numMatrix[y].length;x++){
  					if(numMatrix[y][x]==1){
  						cxt.beginPath();
  						cxt.arc(offsetX+RADIUS+RADIUS*2*x,offsetY+RADIUS+RADIUS*2*y,RADIUS,0,2*Math.PI);
  						cxt.fill();
  					}
  				}
  			}
  			cxt.beginPath();
  			offsetX += numMatrix[0].length*RADIUS*2;
  			return offsetX;
  		}

  		var canvas = document.getElementById("canvas");
  		canvas.width=WINDOW_WIDTH;
  		canvas.height=WINDOW_HEIGHT;
  		context = canvas.getContext("2d");

  		//记录当前绘制的时刻
  		var currentDate = new Date();

  		setInterval(function(){
  			//清空整个Canvas，重新绘制内容
  			context.clearRect(0, 0, context.canvas.width, context.canvas.height);
  			drawDatetime(context);
  		}, 50)
})();
</script>

          
          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#android%E7%99%BE%E9%97%AE%E7%99%BE%E7%AD%94-%E9%82%A3%E4%BA%9B%E5%B9%B4%E8%A2%AB%E9%97%AE%E8%BF%87%E7%9A%84handler%E5%8E%9F%E7%90%86"><span class="nav-number">1.</span> <span class="nav-text">Android百问百答-《那些年被问过的Handler原理》</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#handler%E5%B8%B8%E8%A7%81%E6%8F%90%E9%97%AE"><span class="nav-number">1.1.</span> <span class="nav-text">Handler常见提问</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#handler%E5%B8%B8%E8%A7%81follow-up"><span class="nav-number">1.2.</span> <span class="nav-text">Handler常见Follow Up</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#handler%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="nav-number">1.3.</span> <span class="nav-text">Handler源码分析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#handler%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%8E%9F%E7%90%86"><span class="nav-number">1.3.1.</span> <span class="nav-text">Handler#构造函数原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#handlerobtainmessage-%E5%8E%9F%E7%90%86"><span class="nav-number">1.3.2.</span> <span class="nav-text">Handler#obtainMessage 原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#message%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="nav-number">1.3.3.</span> <span class="nav-text">Message源码分析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#handlerpost%E7%9A%84%E5%8E%9F%E7%90%86"><span class="nav-number">1.3.4.</span> <span class="nav-text">Handler#post的原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#handlersendmessage%E5%8E%9F%E7%90%86"><span class="nav-number">1.3.5.</span> <span class="nav-text">Handler#sendMessage原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#handlerpostdelayed%E5%8E%9F%E7%90%86"><span class="nav-number">1.3.6.</span> <span class="nav-text">Handler#postDelayed原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#handlersendmessagedelayed%E5%8E%9F%E7%90%86"><span class="nav-number">1.3.7.</span> <span class="nav-text">Handler#sendMessageDelayed原理</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#messagequeue%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="nav-number">1.4.</span> <span class="nav-text">MessageQueue源码分析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#messagequeue%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%8E%9F%E7%90%86"><span class="nav-number">1.4.1.</span> <span class="nav-text">MessageQueue#构造函数原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#messagequeueenqueuemessage%E5%8E%9F%E7%90%86"><span class="nav-number">1.4.2.</span> <span class="nav-text">MessageQueue#enqueueMessage原理</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#looper%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="nav-number">1.5.</span> <span class="nav-text">Looper源码分析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#looper%E5%AD%90%E7%BA%BF%E7%A8%8B%E7%94%A8%E4%BE%8B"><span class="nav-number">1.5.1.</span> <span class="nav-text">Looper#子线程用例</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#looper%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%8E%9F%E7%90%86"><span class="nav-number">1.5.2.</span> <span class="nav-text">Looper#构造函数原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#loopermylooper%E5%8E%9F%E7%90%86"><span class="nav-number">1.5.3.</span> <span class="nav-text">Looper#myLooper()原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#looperprepare%E5%8E%9F%E7%90%86"><span class="nav-number">1.5.4.</span> <span class="nav-text">Looper#prepare原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#looperloop%E5%8E%9F%E7%90%86"><span class="nav-number">1.5.5.</span> <span class="nav-text">Looper#loop原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#handlerdispatchmessage"><span class="nav-number">1.5.6.</span> <span class="nav-text">Handler#dispatchMessage</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%9D%A2%E8%AF%95%E9%A2%98%E8%A7%A3%E6%9E%90"><span class="nav-number">1.6.</span> <span class="nav-text">面试题解析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%93%AA%E4%BA%9B%E5%9C%BA%E6%99%AF%E4%BD%BF%E7%94%A8%E5%88%B0%E4%BA%86handler%E7%94%A8handler%E5%81%9A%E4%BB%80%E4%B9%88%E4%B8%9A%E5%8A%A1"><span class="nav-number">1.6.1.</span> <span class="nav-text">哪些场景使用到了Handler？用Handler做什么业务？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84%E6%B6%88%E6%81%AF%E5%8F%91%E9%80%81"><span class="nav-number">1.6.1.1.</span> <span class="nav-text">最简单的消息发送</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%BB%93%E5%90%88handlerthread%E5%A4%84%E7%90%86%E8%80%97%E6%97%B6%E4%BB%BB%E5%8A%A1"><span class="nav-number">1.6.1.2.</span> <span class="nav-text">结合HandlerThread处理耗时任务</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%80%92%E8%AE%A1%E6%97%B6view%E7%9A%84%E7%AE%80%E6%98%93%E5%AE%9E%E7%8E%B0"><span class="nav-number">1.6.1.3.</span> <span class="nav-text">倒计时View的简易实现</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%BB%93%E5%90%88intentservice%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="nav-number">1.6.1.4.</span> <span class="nav-text">结合IntentService的使用</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#idlehandler-%E7%94%A8%E4%BA%8Eui%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96"><span class="nav-number">1.6.1.5.</span> <span class="nav-text">IdleHandler 用于UI性能优化</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#handlerthread%E7%94%A8%E4%BA%8E%E5%8D%95%E7%BA%BF%E7%A8%8B%E6%B6%88%E6%81%AF%E9%80%9A%E7%9F%A5%E5%99%A8"><span class="nav-number">1.6.1.6.</span> <span class="nav-text">HandlerThread用于单线程消息通知器</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%94%A8handler%E9%81%87%E5%88%B0%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3%E8%BF%99%E4%BA%9B%E9%97%AE%E9%A2%98%E7%9A%84"><span class="nav-number">1.6.2.</span> <span class="nav-text">用Handler遇到什么问题？怎么解决这些问题的？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%97%AE%E9%A2%98handler%E5%BB%B6%E6%97%B6%E4%B8%8D%E5%87%86%E7%A1%AE%E7%BB%8F%E5%B8%B8%E5%88%B0%E4%BA%86%E6%97%B6%E9%97%B4%E4%B8%8D%E5%93%8D%E5%BA%94%E4%B8%9A%E5%8A%A1"><span class="nav-number">1.6.2.1.</span> <span class="nav-text">问题：Handler延时不准确，经常到了时间不响应业务</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%97%AE%E9%A2%98%E5%AD%90%E7%BA%BF%E7%A8%8B%E5%88%9B%E5%BB%BAhandler%E5%A4%B1%E8%B4%A5"><span class="nav-number">1.6.2.2.</span> <span class="nav-text">问题：子线程创建Handler失败</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%97%AE%E9%A2%98%E9%9D%9E%E9%9D%99%E6%80%81%E7%B1%BB%E5%AF%BC%E8%87%B4%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F"><span class="nav-number">1.6.2.3.</span> <span class="nav-text">问题：非静态类导致的内存泄漏</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AF%B4%E4%B8%80%E8%AF%B4handler%E5%8E%9F%E7%90%86"><span class="nav-number">1.6.3.</span> <span class="nav-text">说一说Handler原理？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%B8%80%E6%AD%A5%E5%9B%9E%E7%AD%94handler%E6%98%AF%E4%BB%80%E4%B9%88%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9C%BA%E6%99%AF"><span class="nav-number">1.6.3.1.</span> <span class="nav-text">第一步：回答Handler是什么，有哪些场景。</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%BA%8C%E6%AD%A5handler%E7%9A%84%E5%85%B3%E9%94%AEapi%E6%98%AF%E4%BB%80%E4%B9%88%E7%94%A8%E9%80%94%E6%98%AF%E4%BB%80%E4%B9%88%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E7%9A%84"><span class="nav-number">1.6.3.2.</span> <span class="nav-text">第二步：Handler的关键API是什么，用途是什么，如何实现的。</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AC%AC%E4%B8%89%E6%AD%A5handler%E7%9A%84%E5%85%B3%E9%94%AE%E5%AF%B9%E8%B1%A1%E6%98%AF%E4%BB%80%E4%B9%88%E6%8F%90%E4%BE%9B%E5%93%AA%E4%BA%9B%E5%8A%9F%E8%83%BD%E8%BF%99%E4%BA%9B%E5%8A%9F%E8%83%BD%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E7%9A%84"><span class="nav-number">1.6.3.3.</span> <span class="nav-text">第三步：Handler的关键对象是什么，提供哪些功能，这些功能如何实现的。</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%83%BD%E8%87%AA%E5%B7%B1%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AAhandler%E5%90%97"><span class="nav-number">1.6.4.</span> <span class="nav-text">能自己实现一个Handler吗？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AE%80%E7%89%88handler"><span class="nav-number">1.6.4.1.</span> <span class="nav-text">简版Handler</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AE%80%E7%89%88looper"><span class="nav-number">1.6.4.2.</span> <span class="nav-text">简版Looper</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AE%80%E7%89%88messagequeue"><span class="nav-number">1.6.4.3.</span> <span class="nav-text">简版MessageQueue</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AE%80%E7%89%88message"><span class="nav-number">1.6.4.4.</span> <span class="nav-text">简版Message</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%86%99%E4%B8%80%E4%B8%AA%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E7%9A%84%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B"><span class="nav-number">1.6.4.5.</span> <span class="nav-text">写一个网络请求的测试用例</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AF%B4%E4%B8%80%E8%AF%B4handler%E5%BB%B6%E6%97%B6%E5%8E%9F%E7%90%86"><span class="nav-number">1.6.5.</span> <span class="nav-text">说一说Handler延时原理？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#handler%E5%BB%B6%E6%97%B6%E6%9C%89%E5%93%AA%E4%BA%9B%E7%BC%BA%E9%99%B7%E9%80%A0%E6%88%90%E8%BF%99%E4%BA%9B%E7%BC%BA%E9%99%B7%E7%9A%84%E5%8E%9F%E5%9B%A0"><span class="nav-number">1.6.6.</span> <span class="nav-text">Handler延时有哪些缺陷？造成这些缺陷的原因？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AD%90%E7%BA%BF%E7%A8%8B%E8%83%BD%E4%BD%BF%E7%94%A8handler%E5%90%97"><span class="nav-number">1.6.7.</span> <span class="nav-text">子线程能使用Handler吗？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AD%90%E7%BA%BF%E7%A8%8B%E8%83%BD%E5%88%9B%E5%BB%BAhandler%E5%90%97"><span class="nav-number">1.6.8.</span> <span class="nav-text">子线程能创建Handler吗？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AD%90%E7%BA%BF%E7%A8%8B%E5%88%9B%E5%BB%BAhandler%E8%BF%99%E4%B9%88%E9%BA%BB%E7%83%A6%E6%9C%89%E4%BB%80%E4%B9%88%E6%9B%BF%E4%BB%A3%E6%96%B9%E6%B3%95%E5%90%97%E4%BA%86%E8%A7%A3%E8%BF%87handlerthread%E5%90%97"><span class="nav-number">1.6.9.</span> <span class="nav-text">子线程创建Handler这么麻烦，有什么替代方法吗？了解过HandlerThread吗？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BA%86%E8%A7%A3%E8%BF%87idlehandler%E5%90%97"><span class="nav-number">1.6.10.</span> <span class="nav-text">了解过IdleHandler吗？</span></a></li></ol></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

      

      <!-- 标签云 -->
      <!--
      
      <script type="text/javascript" charset="utf-8" src="/js/tagcloud.js"></script>
      <script type="text/javascript" charset="utf-8" src="/js/tagcanvas.js"></script>
      <div class="widget-wrap">
      <h3 class="widget-title">Tag Cloud</h3>
      <div id="myCanvasContainer" class="widget tagcloud">
          <canvas width="250" height="250" id="resCanvas" style="width=100%">
              <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Android/" rel="tag">Android</a><span class="tag-list-count">13</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/AndroidUI%E4%BC%98%E5%8C%96/" rel="tag">AndroidUI优化</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Android%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/" rel="tag">Android性能优化</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/BugFree/" rel="tag">BugFree</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Communication/" rel="tag">Communication</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Handler/" rel="tag">Handler</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Performance/" rel="tag">Performance</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E4%B8%AA%E4%BA%BA%E6%95%99%E8%82%B2/" rel="tag">个人教育</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95/" rel="tag">学习方法</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6/" rel="tag">开源框架</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%8A%80%E6%9C%AF%E9%80%89%E5%9E%8B/" rel="tag">技术选型</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%9D%82%E8%B0%88/" rel="tag">杂谈</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E7%A8%8B%E5%BA%8F%E4%BA%BA%E7%94%9F%EF%BC%8C%E6%9D%82%E8%B0%88/" rel="tag">程序人生，杂谈</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E7%AE%97%E6%B3%95/" rel="tag">算法</a><span class="tag-list-count">1</span></li></ul>
          </canvas>
      </div>
      </div>
      
      -->
      <!-- 标签云 -->

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<div class="copyright">&copy; 2015 &mdash; <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
       <i class="fa fa-heartbeat"></i>
  </span>
  <!--
    <span class="author" itemprop="copyrightHolder"> &nbsp;叶金诚</span>
  -->
  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">111k</span>
  
</div>














  <script src='https://unpkg.com/mermaid@7.1.2/dist/mermaid.min.js'></script>
  <script>
    if (window.mermaid) {
      mermaid.initialize("");
    }
  </script>



        
<div class="busuanzi-count">
  <!--
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>
  -->
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

   

  
  
    
      <span class="site-uv">
        
        我的第 <span class="busuanzi-value" id="busuanzi_value_site_uv"></span> 位朋友，
      </span>
    

    
      <span class="site-pv">
        历经 <span class="busuanzi-value" id="busuanzi_value_site_pv"></span> 次回眸才与你相遇
        <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      </span>
    
  

</div>








        
      </div>
    </footer>

    
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  










  



  
  
    <script type="text/javascript" src="//cdn.jsdelivr.net/npm/jquery@2.1.3/dist/jquery.min.js"></script>
  

  
  
    <script type="text/javascript" src="//cdn.jsdelivr.net/fastclick/1.0.6/fastclick.min.js"></script>
  

  
  
    <script type="text/javascript" src="//cdn.jsdelivr.net/jquery.lazyload/1.9.3/jquery.lazyload.min.js"></script>
  

  
  
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/velocity-animate@1.2.1/velocity.min.js"></script>
  

  
  
    <script type="text/javascript" src="//cdn.jsdelivr.net/npm/velocity-animate@1.2.1/velocity.ui.min.js"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  

  
  
    <script id="ribbon" type="text/javascript" size="60" alpha="0.1"  zIndex="-1" src="/blog/lib/canvas-ribbon/canvas-ribbon.js"></script>
  


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/blog/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  















  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/blog/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





    <script>  function addCount(Counter){var $visitors=$('.leancloud_visitors');var url=$visitors.attr('id').trim();var title=$visitors.attr('data-flag-title').trim();Counter('get','/classes/Counter',{where:JSON.stringify({url})}).done(function({results}){if(results.length>0){var counter=results[0]; var $element=$(document.getElementById(url));$element.find('.leancloud-visitors-count').text(counter.time+1);Counter('put','/classes/Counter/'+counter.objectId,JSON.stringify({time:{'__op':'Increment','amount':1}})) .fail(function({responseJSON}){console.log('Failed to save Visitor num, with error message: '+responseJSON.error);})}else{ Counter('post','/classes/Counter',JSON.stringify({title:title,url:url,time:1})).done(function(){var $element=$(document.getElementById(url));$element.find('.leancloud-visitors-count').text(1);}).fail(function(){console.log('Failed to create');});}}).fail(function({responseJSON}){console.log('LeanCloud Counter Error: '+responseJSON.code+' '+responseJSON.error);});}$(function(){$.get('https://app-router.leancloud.cn/2/route?appId='+'MFn6psRe8xtnq9sq6lv9MJks-gzGzoHsz').done(function({api_server}){var Counter=function(method,url,data){return $.ajax({method:method,url:'https://'+api_server+'/1.1'+url,headers:{'X-LC-Id':'MFn6psRe8xtnq9sq6lv9MJks-gzGzoHsz','X-LC-Key':'pi8Pa2aH3XpcaovMyqIXVuOP','Content-Type':'application/json',},data:data});};  const localhost=/http:\/\/(localhost|127.0.0.1|0.0.0.0)/;if(localhost.test(document.URL))return;addCount(Counter);});});</script>

  

  
<script>
(function(){
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
    }
    else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>


  
  
  
  <link rel="stylesheet" href="/blog/lib/needsharebutton/needsharebutton.css">

  
  
  <script src="/blog/lib/needsharebutton/needsharebutton.js"></script>

  <script>
    
      pbOptions = {};
      
          pbOptions.iconStyle = "default";
      
          pbOptions.boxForm = "horizontal";
      
          pbOptions.position = "bottomCenter";
      
          pbOptions.networks = "Weibo,Wechat,Douban,QQZone,Twitter,Facebook";
      
      new needShareButton('#needsharebutton-postbottom', pbOptions);
    
    
  </script>

  

  
  


  

  


  <!-- Tidio 在线联系功能、鼠标点击特效、页面反馈...-->
  <script type="text/javascript" src="/js/src/clipboard.min.js"></script>
<script type="text/javascript" src="/js/src/clipboard-use.js"></script>



  
    <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas>
    <script async src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script>
    <script async src="/js/cursor/explosion.min.js"></script>
  










  <script src="/js/src/activate-power-mode.min.js"></script>
  <script>
    POWERMODE.colorful = true;
    POWERMODE.shake = false;
    document.body.addEventListener('input', POWERMODE);
  </script>






  <script async language="javascript">

    var div = document.createElement("div");
    //插入到自定义的theme-info或者copyright之后
    var copyright = document.querySelector(".theme-info2") || document.querySelector(".copyright");

    function show_run_time(){
        window.setTimeout("show_run_time()", 1000);
      // BirthDay=new Date("08/07/2019 20:00:00");
        BirthDay=new Date("08/07/2016 20:00:00");
        today=new Date();
        timeold=(today.getTime()-BirthDay.getTime());
        sectimeold=timeold/1000
        secondsold=Math.floor(sectimeold);
        msPerDay=24*60*60*1000
        e_daysold=timeold/msPerDay
        daysold=Math.floor(e_daysold);
        e_hrsold=(e_daysold-daysold)*24;
        hrsold=setzero(Math.floor(e_hrsold));
        e_minsold=(e_hrsold-hrsold)*60;
        minsold=setzero(Math.floor((e_hrsold-hrsold)*60));
        seconds=setzero(Math.floor((e_minsold-minsold)*60));

        // 使用zh-Hans.yml的文字替换
        div.innerHTML = "我已在此等候你 " + "<span style='color: #1890ff'> " + daysold + " </span> 天 <span style='color: #1890ff'>" + hrsold + " </span>时 <span style='color: #1890ff'>" + minsold + " </span>分 <span style='color: #1890ff'>" + seconds + " </span>秒 ";

        document.querySelector(".footer-inner").insertBefore(div, copyright.nextSibling);

    }
    function setzero(i){
        if (i<10)
        {i="0" + i};
        return i;
    }

    show_run_time();

  </script>




<!-- 旋转魔方 -->

   
      
<style>
  /*最外层容器样式*/
  .wrap {
    width: 0px;
    height: 0px;
    /*margin: 80px;*/
    /*position: relative;*/
	     /*显示位置*/
    position: fixed;
    bottom: 120px;
    right: 240px;
    z-index: 999;
  }

  /*包裹所有容器样式*/
  .cube {
    width: 0px;
    height: 0px;
    margin: 0 auto;
    -webkit-transform-style: preserve-3d;
    transform-style: preserve-3d;
    transform: rotateX(-30deg) rotateY(-80deg);
    animation: rotate linear 10s infinite;
  }

  @-webkit-keyframes rotate {
    from {
      transform: rotateX(0deg) rotateY(0deg);
    }
    to {
      transform: rotateX(360deg) rotateY(360deg);
    }
  }

  .cube div {
    position: absolute;
    width: 50px;
    height: 50px;
    opacity: 0.8;
    transition: all .4s;
  }

  /*定义所有图片样式*/
  .pic {
    width: 50px;
    height: 50px;
  }

  .cube .out_front {
    transform: rotateY(0deg) translateZ(25px);
  }

  .cube .out_back {
    transform: translateZ(-25px) rotateY(180deg);
  }

  .cube .out_left {
    transform: rotateY(-90deg) translateZ(25px);
  }

  .cube .out_right {
    transform: rotateY(90deg) translateZ(25px);
  }

  .cube .out_top {
    transform: rotateX(90deg) translateZ(25px);
  }

  .cube .out_bottom {
    transform: rotateX(-90deg) translateZ(25px);
  }

  /*定义小正方体样式*/
  .cube span {
    display: block;
    width: 25px;
    height: 25px;
    position: absolute;
    top: 12px;
    left: 12px;
  }

  .cube .in_pic {
    width: 25px;
    height:25px;
  }

  .cube .in_front {
    transform: rotateY(0deg) translateZ(12px);
  }

  .cube .in_back {
    transform: translateZ(-12px) rotateY(180deg);
  }

  .cube .in_left {
    transform: rotateY(-90deg) translateZ(12px);
  }

  .cube .in_right {
    transform: rotateY(90deg) translateZ(12px);
  }

  .cube .in_top {
    transform: rotateX(90deg) translateZ(12px);
  }

  .cube .in_bottom {
    transform: rotateX(-90deg) translateZ(12px);
  }

  /*鼠标移入后样式*/
  .cube:hover .out_front {
    transform: rotateY(0deg) translateZ(50px);
  }

  .cube:hover .out_back {
    transform: translateZ(-50px) rotateY(180deg);
  }

  .cube:hover .out_left {
    transform: rotateY(-90deg) translateZ(50px);
  }

  .cube:hover .out_right {
    transform: rotateY(90deg) translateZ(50px);
  }

  .cube:hover .out_top {
    transform: rotateX(90deg) translateZ(50px);
  }

  .cube:hover .out_bottom {
    transform: rotateX(-90deg) translateZ(50px);
  }
</style>

<div class="wrap">

		<!--包裹所有元素的容器-->
		<div class="cube">
			<!--前面图片 -->
			<div class="out_front">
			  <a onclick="back2top()">
				  <img src="http://cdn.yangchaofan.cn/BlogGifRes/20210307/lAq9mKTioPeO.png" class="pic" />
				</a>
			</div>
			<!--后面图片 -->
			<div class="out_back">
				<a onclick="back2top()">
          <img src="http://cdn.yangchaofan.cn/BlogGifRes/20210307/lAq9mKTioPeO.png" class="pic" />
        </a>
			</div>
			<!--左面图片 -->
			<div class="out_left">
        <a onclick="back2top()">
				  <img src="http://cdn.yangchaofan.cn/BlogGifRes/20210307/lAq9mKTioPeO.png" class="pic" />
				</a>
			</div>
			<!--右面图片 -->
			<div class="out_right">
				<a onclick="back2top()">
          <img src="http://cdn.yangchaofan.cn/BlogGifRes/20210307/lAq9mKTioPeO.png" class="pic" />
        </a>
			</div>
			<!--上面图片 -->
			<div class="out_top">
				<a onclick="back2top()">
          <img src="http://cdn.yangchaofan.cn/BlogGifRes/20210307/lAq9mKTioPeO.png" class="pic" />
        </a>
			</div>
			<!--下面图片 -->
			<div class="out_bottom">
				<a onclick="back2top()">
          <img src="http://cdn.yangchaofan.cn/BlogGifRes/20210307/lAq9mKTioPeO.png" class="pic" />
        </a>
			</div>

			<!--小正方体 -->
			<span class="in_front">
				<img src="https://s2.ax1x.com/2019/10/14/KptCPf.th.jpg" class="in_pic" />
			</span>
			<span class="in_back">
			     <img src="https://s2.ax1x.com/2019/10/14/KptCPf.th.jpg" class="in_pic" />
			</span>
			<span class="in_left">
				<img src="https://s2.ax1x.com/2019/10/14/KptCPf.th.jpg" class="in_pic" />
			</span>
			<span class="in_right">
				<img src="https://s2.ax1x.com/2019/10/14/KptCPf.th.jpg" class="in_pic" />
			</span>
			<span class="in_top">
				<img src="https://s2.ax1x.com/2019/10/14/KptCPf.th.jpg" class="in_pic" />
			</span>
			<span class="in_bottom">
				<img src="https://s2.ax1x.com/2019/10/14/KptCPf.th.jpg" class="in_pic" />
			</span>
		</div>

</div>

<script>
  function back2top(){
    $('html, body').animate({scrollTop: 0}, 500);
  }
</script>

   


<!-- Console 输出第三方个性化字体 -->

  <script async type="text/javascript" src="/blog/figlet/fetch.min.js"></script>
  <script type="text/javascript" src="/blog/figlet/figlet.js"></script>
  <script type="text/javascript">

      figlet.defaults({fontPath: "/figlet/fonts"});
      figlet("Welcome To Leaface", "Big Money-ne", function(err, text) {
          if (err) {
              console.log("something went wrong...");
              console.dir(err);
              return;
          }
          console.log(text);
      });
  </script>


  <!-- Console 输出自定义字体 -->
  
    <script async type="text/javascript">
        var text = "Welcome To Leaface";
        var date = '2021-06-10';
        console.log("%c " + text, "font-size:100px;color:white;border-radius:20px;height:200px; background:-webkit-linear-gradient(yellow,orange,red,green,blue,purple);text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9,0 3px 0 #bbb,0 4px 0 #b9b9b9,0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1),0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15);");
        console.info('\n' + ' %c Leafae Site %c https://www.liaofuzhan.com ' + '\n', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
        console.info('\n' + ' %c Leafae QQ %c 793458585 ' + '\n', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
        console.info('\n' + ' %c Leafae Wechat %c leaface ' + '\n', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
       // console.log("%c Time: " + date, "font-size:100px;white:"+fcolor+";border-radius:20px;height:200px; background:-webkit-linear-gradient(yellow,orange,red,green,blue,purple);text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9,0 3px 0 #bbb,0 4px 0 #b9b9b9,0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1),0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15);  background-image: linear-gradient(to right, orangered, orange, gold, lightgreen, cyan, dodgerblue, mediumpurple, hotpink, orangered);");
       // console.log("%c .", "padding:300px 600px;line-height:10px;background:url(https://s2.ax1x.com/2019/10/17/KkoAJJ.md.png) no-repeat;");
    </script>
  


  <!-- 看板娘 -->
  
      <script async src="/live2d-widget/autoload.js"></script>
  
	<!-- leafacePlayer -->
	
	<script id="lfz" src="http://player.liaofuzhan.com/player/player.js" key=1368557075310858241></script>
	

  

  

  <!-- 代码块复制功能 -->
  <script async type="text/javascript" src="/js/src/clipboard.min.js"></script>
  <script async type="text/javascript" src="/js/src/clipboard-use.js"></script>
 

  <!--share.js-->
  <link async rel="stylesheet" href="/sharejs/css/share.min.css">
  <script async src="/sharejs/js/social-share.min.js"></script>

  <!-- 模仿知乎卡片样式链接、崩溃欺骗特效 -->
  <script async type="text/javascript" src="/js/src/linkcard.js"></script>

  <!--崩溃欺骗 放在js文件最后-->
  <!--
  <script type="text/javascript" src="/js/src/crash_cheat.js"></script>
  -->

 

<script src="/blog/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/","tagMode":false});</script></body>
</html>
